github.com/ilhicas/nomad@v0.11.8/drivers/docker/reconciler_test.go (about) 1 package docker 2 3 import ( 4 "encoding/json" 5 "os" 6 "testing" 7 "time" 8 9 docker "github.com/fsouza/go-dockerclient" 10 "github.com/hashicorp/nomad/client/testutil" 11 "github.com/hashicorp/nomad/helper/freeport" 12 "github.com/hashicorp/nomad/helper/uuid" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func fakeContainerList(t *testing.T) (nomadContainer, nonNomadContainer docker.APIContainers) { 17 path := "./test-resources/docker/reconciler_containers_list.json" 18 19 f, err := os.Open(path) 20 if err != nil { 21 t.Fatalf("failed to open file: %v", err) 22 } 23 24 var sampleContainerList []docker.APIContainers 25 err = json.NewDecoder(f).Decode(&sampleContainerList) 26 if err != nil { 27 t.Fatalf("failed to decode container list: %v", err) 28 } 29 30 return sampleContainerList[0], sampleContainerList[1] 31 } 32 33 func Test_HasMount(t *testing.T) { 34 nomadContainer, nonNomadContainer := fakeContainerList(t) 35 36 require.True(t, hasMount(nomadContainer, "/alloc")) 37 require.True(t, hasMount(nomadContainer, "/data")) 38 require.True(t, hasMount(nomadContainer, "/secrets")) 39 require.False(t, hasMount(nomadContainer, "/random")) 40 41 require.False(t, hasMount(nonNomadContainer, "/alloc")) 42 require.False(t, hasMount(nonNomadContainer, "/data")) 43 require.False(t, hasMount(nonNomadContainer, "/secrets")) 44 require.False(t, hasMount(nonNomadContainer, "/random")) 45 } 46 47 func Test_HasNomadName(t *testing.T) { 48 nomadContainer, nonNomadContainer := fakeContainerList(t) 49 50 require.True(t, hasNomadName(nomadContainer)) 51 require.False(t, hasNomadName(nonNomadContainer)) 52 } 53 54 // TestDanglingContainerRemoval asserts containers without corresponding tasks 55 // are removed after the creation grace period. 56 func TestDanglingContainerRemoval(t *testing.T) { 57 testutil.DockerCompatible(t) 58 59 // start two containers: one tracked nomad container, and one unrelated container 60 task, cfg, ports := dockerTask(t) 61 defer freeport.Return(ports) 62 require.NoError(t, task.EncodeConcreteDriverConfig(cfg)) 63 64 client, d, handle, cleanup := dockerSetup(t, task, nil) 65 defer cleanup() 66 require.NoError(t, d.WaitUntilStarted(task.ID, 5*time.Second)) 67 68 nonNomadContainer, err := client.CreateContainer(docker.CreateContainerOptions{ 69 Name: "mytest-image-" + uuid.Generate(), 70 Config: &docker.Config{ 71 Image: cfg.Image, 72 Cmd: append([]string{cfg.Command}, cfg.Args...), 73 }, 74 }) 75 require.NoError(t, err) 76 defer client.RemoveContainer(docker.RemoveContainerOptions{ 77 ID: nonNomadContainer.ID, 78 Force: true, 79 }) 80 81 err = client.StartContainer(nonNomadContainer.ID, nil) 82 require.NoError(t, err) 83 84 untrackedNomadContainer, err := client.CreateContainer(docker.CreateContainerOptions{ 85 Name: "mytest-image-" + uuid.Generate(), 86 Config: &docker.Config{ 87 Image: cfg.Image, 88 Cmd: append([]string{cfg.Command}, cfg.Args...), 89 Labels: map[string]string{ 90 dockerLabelAllocID: uuid.Generate(), 91 }, 92 }, 93 }) 94 require.NoError(t, err) 95 defer client.RemoveContainer(docker.RemoveContainerOptions{ 96 ID: untrackedNomadContainer.ID, 97 Force: true, 98 }) 99 100 err = client.StartContainer(untrackedNomadContainer.ID, nil) 101 require.NoError(t, err) 102 103 dd := d.Impl().(*Driver) 104 105 reconciler := newReconciler(dd) 106 trackedContainers := map[string]bool{handle.containerID: true} 107 108 tf := reconciler.trackedContainers() 109 require.Contains(t, tf, handle.containerID) 110 require.NotContains(t, tf, untrackedNomadContainer) 111 require.NotContains(t, tf, nonNomadContainer.ID) 112 113 // assert tracked containers should never be untracked 114 untracked, err := reconciler.untrackedContainers(trackedContainers, time.Now()) 115 require.NoError(t, err) 116 require.NotContains(t, untracked, handle.containerID) 117 require.NotContains(t, untracked, nonNomadContainer.ID) 118 require.Contains(t, untracked, untrackedNomadContainer.ID) 119 120 // assert we recognize nomad containers with appropriate cutoff 121 untracked, err = reconciler.untrackedContainers(map[string]bool{}, time.Now()) 122 require.NoError(t, err) 123 require.Contains(t, untracked, handle.containerID) 124 require.Contains(t, untracked, untrackedNomadContainer.ID) 125 require.NotContains(t, untracked, nonNomadContainer.ID) 126 127 // but ignore if creation happened before cutoff 128 untracked, err = reconciler.untrackedContainers(map[string]bool{}, time.Now().Add(-1*time.Minute)) 129 require.NoError(t, err) 130 require.NotContains(t, untracked, handle.containerID) 131 require.NotContains(t, untracked, untrackedNomadContainer.ID) 132 require.NotContains(t, untracked, nonNomadContainer.ID) 133 134 // a full integration tests to assert that containers are removed 135 prestineDriver := dockerDriverHarness(t, nil).Impl().(*Driver) 136 prestineDriver.config.GC.DanglingContainers = ContainerGCConfig{ 137 Enabled: true, 138 period: 1 * time.Second, 139 CreationGrace: 0 * time.Second, 140 } 141 nReconciler := newReconciler(prestineDriver) 142 143 require.NoError(t, nReconciler.removeDanglingContainersIteration()) 144 145 _, err = client.InspectContainer(nonNomadContainer.ID) 146 require.NoError(t, err) 147 148 _, err = client.InspectContainer(handle.containerID) 149 require.Error(t, err) 150 require.Contains(t, err.Error(), NoSuchContainerError) 151 152 _, err = client.InspectContainer(untrackedNomadContainer.ID) 153 require.Error(t, err) 154 require.Contains(t, err.Error(), NoSuchContainerError) 155 } 156 157 // TestDanglingContainerRemoval_Stopped asserts stopped containers without 158 // corresponding tasks are not removed even if after creation grace period. 159 func TestDanglingContainerRemoval_Stopped(t *testing.T) { 160 testutil.DockerCompatible(t) 161 162 _, cfg, ports := dockerTask(t) 163 defer freeport.Return(ports) 164 165 client := newTestDockerClient(t) 166 container, err := client.CreateContainer(docker.CreateContainerOptions{ 167 Name: "mytest-image-" + uuid.Generate(), 168 Config: &docker.Config{ 169 Image: cfg.Image, 170 Cmd: append([]string{cfg.Command}, cfg.Args...), 171 Labels: map[string]string{ 172 dockerLabelAllocID: uuid.Generate(), 173 }, 174 }, 175 }) 176 require.NoError(t, err) 177 defer client.RemoveContainer(docker.RemoveContainerOptions{ 178 ID: container.ID, 179 Force: true, 180 }) 181 182 err = client.StartContainer(container.ID, nil) 183 require.NoError(t, err) 184 185 err = client.StopContainer(container.ID, 60) 186 require.NoError(t, err) 187 188 dd := dockerDriverHarness(t, nil).Impl().(*Driver) 189 reconciler := newReconciler(dd) 190 191 // assert nomad container is tracked, and we ignore stopped one 192 tf := reconciler.trackedContainers() 193 require.NotContains(t, tf, container.ID) 194 195 untracked, err := reconciler.untrackedContainers(map[string]bool{}, time.Now()) 196 require.NoError(t, err) 197 require.NotContains(t, untracked, container.ID) 198 199 // if we start container again, it'll be marked as untracked 200 require.NoError(t, client.StartContainer(container.ID, nil)) 201 202 untracked, err = reconciler.untrackedContainers(map[string]bool{}, time.Now()) 203 require.NoError(t, err) 204 require.Contains(t, untracked, container.ID) 205 }