docker driver: handle already deleted containers

don't expect that all the containers of a pod are available. Some could be
already be removed. Improve the logic of sorting containers by their index.
This commit is contained in:
Simone Gotti 2019-05-15 15:55:08 +02:00
parent 821e371cd8
commit faded9b809
2 changed files with 87 additions and 13 deletions

View File

@ -23,6 +23,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"runtime" "runtime"
"sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -181,10 +182,9 @@ func (d *DockerDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.
id: podConfig.ID, id: podConfig.ID,
client: d.client, client: d.client,
executorID: d.executorID, executorID: d.executorID,
containers: make([]types.Container, len(containers)), containers: []*DockerContainer{},
} }
// Put the containers in the right order based on their containerIndexKey label value
count := 0 count := 0
seenIndexes := map[int]struct{}{} seenIndexes := map[int]struct{}{}
for _, container := range containers { for _, container := range containers {
@ -201,7 +201,11 @@ func (d *DockerDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.
if _, ok := seenIndexes[cIndex]; ok { if _, ok := seenIndexes[cIndex]; ok {
return nil, errors.Errorf("duplicate container with index %d", cIndex) return nil, errors.Errorf("duplicate container with index %d", cIndex)
} }
pod.containers[cIndex] = container dContainer := &DockerContainer{
Index: cIndex,
Container: container,
}
pod.containers = append(pod.containers, dContainer)
seenIndexes[cIndex] = struct{}{} seenIndexes[cIndex] = struct{}{}
count++ count++
@ -209,6 +213,8 @@ func (d *DockerDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.
if count != len(containers) { if count != len(containers) {
return nil, errors.Errorf("expected %d containers but got %d", len(containers), count) return nil, errors.Errorf("expected %d containers but got %d", len(containers), count)
} }
// put the containers in the right order based on their container index
sort.Sort(ContainerSlice(pod.containers))
return pod, nil return pod, nil
} }
@ -312,21 +318,17 @@ func (d *DockerDriver) GetPods(ctx context.Context, all bool) ([]Pod, error) {
// skip container // skip container
continue continue
} }
if pod, ok := podsMap[podID]; !ok { if _, ok := podsMap[podID]; !ok {
pod := &DockerPod{ pod := &DockerPod{
id: podID, id: podID,
client: d.client, client: d.client,
containers: []types.Container{container},
executorID: d.executorID, executorID: d.executorID,
containers: []*DockerContainer{},
} }
podsMap[podID] = pod podsMap[podID] = pod
} else {
pod.containers = append(pod.containers, container)
} }
} }
// Put the containers in the right order based on their containerIndexKey label value
for _, container := range containers { for _, container := range containers {
podID, ok := container.Labels[podIDKey] podID, ok := container.Labels[podIDKey]
if !ok { if !ok {
@ -343,8 +345,13 @@ func (d *DockerDriver) GetPods(ctx context.Context, all bool) ([]Pod, error) {
// remove pod since some of its containers don't have the right labels // remove pod since some of its containers don't have the right labels
delete(podsMap, podID) delete(podsMap, podID)
} }
pod := podsMap[podID] pod := podsMap[podID]
pod.containers[cIndex] = container dContainer := &DockerContainer{
Index: cIndex,
Container: container,
}
pod.containers = append(pod.containers, dContainer)
// overwrite containers with the right order // overwrite containers with the right order
@ -363,6 +370,8 @@ func (d *DockerDriver) GetPods(ctx context.Context, all bool) ([]Pod, error) {
pods := make([]Pod, 0, len(podsMap)) pods := make([]Pod, 0, len(podsMap))
for _, pod := range podsMap { for _, pod := range podsMap {
// put the containers in the right order based on their container index
sort.Sort(ContainerSlice(pod.containers))
pods = append(pods, pod) pods = append(pods, pod)
} }
return pods, nil return pods, nil
@ -383,10 +392,21 @@ type DockerPod struct {
id string id string
client *client.Client client *client.Client
labels map[string]string labels map[string]string
containers []types.Container containers []*DockerContainer
executorID string executorID string
} }
type DockerContainer struct {
Index int
types.Container
}
type ContainerSlice []*DockerContainer
func (p ContainerSlice) Len() int { return len(p) }
func (p ContainerSlice) Less(i, j int) bool { return p[i].Index < p[j].Index }
func (p ContainerSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (dp *DockerPod) ID() string { func (dp *DockerPod) ID() string {
return dp.id return dp.id
} }

View File

@ -27,6 +27,7 @@ import (
"time" "time"
"unicode" "unicode"
"github.com/docker/docker/api/types"
uuid "github.com/satori/go.uuid" uuid "github.com/satori/go.uuid"
slog "github.com/sorintlab/agola/internal/log" slog "github.com/sorintlab/agola/internal/log"
@ -294,7 +295,7 @@ func TestDockerPod(t *testing.T) {
dp := p.(*DockerPod) dp := p.(*DockerPod)
for i, c := range dp.containers { for i, c := range dp.containers {
if c.ID != ip.containers[i].ID { if c.ID != ip.containers[i].ID {
t.Fatalf("different pod id, want: %s, got: %s", ip.id, dp.id) t.Fatalf("different container id, want: %q, got: %q", c.ID, ip.containers[i].ID)
} }
if diff := cmp.Diff(ip.containers[i], c); diff != "" { if diff := cmp.Diff(ip.containers[i], c); diff != "" {
t.Error(diff) t.Error(diff)
@ -340,7 +341,7 @@ func TestDockerPod(t *testing.T) {
dp := p.(*DockerPod) dp := p.(*DockerPod)
for i, c := range dp.containers { for i, c := range dp.containers {
if c.ID != ip.containers[i].ID { if c.ID != ip.containers[i].ID {
t.Fatalf("different pod id, want: %s, got: %s", ip.id, dp.id) t.Fatalf("different container id, want: %q, got: %q", c.ID, ip.containers[i].ID)
} }
if diff := cmp.Diff(ip.containers[i], c); diff != "" { if diff := cmp.Diff(ip.containers[i], c); diff != "" {
t.Error(diff) t.Error(diff)
@ -352,4 +353,57 @@ func TestDockerPod(t *testing.T) {
t.Fatalf("pod with id %q not found", pod.ID()) t.Fatalf("pod with id %q not found", pod.ID())
} }
}) })
t.Run("test get pods with two containers and the first already deleted", func(t *testing.T) {
pod, err := d.NewPod(ctx, &PodConfig{
ID: uuid.NewV4().String(),
TaskID: uuid.NewV4().String(),
Containers: []*ContainerConfig{
&ContainerConfig{
Cmd: []string{"cat"},
Image: "busybox",
},
&ContainerConfig{
Image: "nginx:1.16",
},
},
InitVolumeDir: "/tmp/agola",
}, ioutil.Discard)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
defer pod.Remove(ctx)
// delete the first container
dp := pod.(*DockerPod)
if err := dp.client.ContainerRemove(ctx, dp.containers[0].ID, types.ContainerRemoveOptions{Force: true}); err != nil {
t.Fatalf("unexpected err: %v", err)
}
pods, err := d.GetPods(ctx, true)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
ok := false
for _, p := range pods {
if p.ID() == pod.ID() {
ok = true
ip := pod.(*DockerPod)
dp := p.(*DockerPod)
if len(dp.containers) != 1 {
t.Fatalf("expected 1 container, got %d containers", len(dp.containers))
}
if dp.containers[0].ID != ip.containers[1].ID {
t.Fatalf("different container id, want: %q, got: %q", dp.containers[0].ID, ip.containers[1].ID)
}
if diff := cmp.Diff(ip.containers[1], dp.containers[0]); diff != "" {
t.Error(diff)
}
}
}
if !ok {
t.Fatalf("pod with id %q not found", pod.ID())
}
})
} }