1package environment // import "github.com/docker/docker/internal/test/environment" 2 3import ( 4 "context" 5 6 "github.com/docker/docker/api/types" 7 "github.com/docker/docker/api/types/filters" 8 dclient "github.com/docker/docker/client" 9 "github.com/docker/docker/internal/test" 10 "gotest.tools/assert" 11) 12 13var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:jessie"} 14 15type protectedElements struct { 16 containers map[string]struct{} 17 images map[string]struct{} 18 networks map[string]struct{} 19 plugins map[string]struct{} 20 volumes map[string]struct{} 21} 22 23func newProtectedElements() protectedElements { 24 return protectedElements{ 25 containers: map[string]struct{}{}, 26 images: map[string]struct{}{}, 27 networks: map[string]struct{}{}, 28 plugins: map[string]struct{}{}, 29 volumes: map[string]struct{}{}, 30 } 31} 32 33// ProtectAll protects the existing environment (containers, images, networks, 34// volumes, and, on Linux, plugins) from being cleaned up at the end of test 35// runs 36func ProtectAll(t assert.TestingT, testEnv *Execution) { 37 if ht, ok := t.(test.HelperT); ok { 38 ht.Helper() 39 } 40 ProtectContainers(t, testEnv) 41 ProtectImages(t, testEnv) 42 ProtectNetworks(t, testEnv) 43 ProtectVolumes(t, testEnv) 44 if testEnv.OSType == "linux" { 45 ProtectPlugins(t, testEnv) 46 } 47} 48 49// ProtectContainer adds the specified container(s) to be protected in case of 50// clean 51func (e *Execution) ProtectContainer(t assert.TestingT, containers ...string) { 52 if ht, ok := t.(test.HelperT); ok { 53 ht.Helper() 54 } 55 for _, container := range containers { 56 e.protectedElements.containers[container] = struct{}{} 57 } 58} 59 60// ProtectContainers protects existing containers from being cleaned up at the 61// end of test runs 62func ProtectContainers(t assert.TestingT, testEnv *Execution) { 63 if ht, ok := t.(test.HelperT); ok { 64 ht.Helper() 65 } 66 containers := getExistingContainers(t, testEnv) 67 testEnv.ProtectContainer(t, containers...) 68} 69 70func getExistingContainers(t assert.TestingT, testEnv *Execution) []string { 71 if ht, ok := t.(test.HelperT); ok { 72 ht.Helper() 73 } 74 client := testEnv.APIClient() 75 containerList, err := client.ContainerList(context.Background(), types.ContainerListOptions{ 76 All: true, 77 }) 78 assert.NilError(t, err, "failed to list containers") 79 80 var containers []string 81 for _, container := range containerList { 82 containers = append(containers, container.ID) 83 } 84 return containers 85} 86 87// ProtectImage adds the specified image(s) to be protected in case of clean 88func (e *Execution) ProtectImage(t assert.TestingT, images ...string) { 89 if ht, ok := t.(test.HelperT); ok { 90 ht.Helper() 91 } 92 for _, image := range images { 93 e.protectedElements.images[image] = struct{}{} 94 } 95} 96 97// ProtectImages protects existing images and on linux frozen images from being 98// cleaned up at the end of test runs 99func ProtectImages(t assert.TestingT, testEnv *Execution) { 100 if ht, ok := t.(test.HelperT); ok { 101 ht.Helper() 102 } 103 images := getExistingImages(t, testEnv) 104 105 if testEnv.OSType == "linux" { 106 images = append(images, frozenImages...) 107 } 108 testEnv.ProtectImage(t, images...) 109} 110 111func getExistingImages(t assert.TestingT, testEnv *Execution) []string { 112 if ht, ok := t.(test.HelperT); ok { 113 ht.Helper() 114 } 115 client := testEnv.APIClient() 116 filter := filters.NewArgs() 117 filter.Add("dangling", "false") 118 imageList, err := client.ImageList(context.Background(), types.ImageListOptions{ 119 All: true, 120 Filters: filter, 121 }) 122 assert.NilError(t, err, "failed to list images") 123 124 var images []string 125 for _, image := range imageList { 126 images = append(images, tagsFromImageSummary(image)...) 127 } 128 return images 129} 130 131func tagsFromImageSummary(image types.ImageSummary) []string { 132 var result []string 133 for _, tag := range image.RepoTags { 134 if tag != "<none>:<none>" { 135 result = append(result, tag) 136 } 137 } 138 for _, digest := range image.RepoDigests { 139 if digest != "<none>@<none>" { 140 result = append(result, digest) 141 } 142 } 143 return result 144} 145 146// ProtectNetwork adds the specified network(s) to be protected in case of 147// clean 148func (e *Execution) ProtectNetwork(t assert.TestingT, networks ...string) { 149 if ht, ok := t.(test.HelperT); ok { 150 ht.Helper() 151 } 152 for _, network := range networks { 153 e.protectedElements.networks[network] = struct{}{} 154 } 155} 156 157// ProtectNetworks protects existing networks from being cleaned up at the end 158// of test runs 159func ProtectNetworks(t assert.TestingT, testEnv *Execution) { 160 if ht, ok := t.(test.HelperT); ok { 161 ht.Helper() 162 } 163 networks := getExistingNetworks(t, testEnv) 164 testEnv.ProtectNetwork(t, networks...) 165} 166 167func getExistingNetworks(t assert.TestingT, testEnv *Execution) []string { 168 if ht, ok := t.(test.HelperT); ok { 169 ht.Helper() 170 } 171 client := testEnv.APIClient() 172 networkList, err := client.NetworkList(context.Background(), types.NetworkListOptions{}) 173 assert.NilError(t, err, "failed to list networks") 174 175 var networks []string 176 for _, network := range networkList { 177 networks = append(networks, network.ID) 178 } 179 return networks 180} 181 182// ProtectPlugin adds the specified plugin(s) to be protected in case of clean 183func (e *Execution) ProtectPlugin(t assert.TestingT, plugins ...string) { 184 if ht, ok := t.(test.HelperT); ok { 185 ht.Helper() 186 } 187 for _, plugin := range plugins { 188 e.protectedElements.plugins[plugin] = struct{}{} 189 } 190} 191 192// ProtectPlugins protects existing plugins from being cleaned up at the end of 193// test runs 194func ProtectPlugins(t assert.TestingT, testEnv *Execution) { 195 if ht, ok := t.(test.HelperT); ok { 196 ht.Helper() 197 } 198 plugins := getExistingPlugins(t, testEnv) 199 testEnv.ProtectPlugin(t, plugins...) 200} 201 202func getExistingPlugins(t assert.TestingT, testEnv *Execution) []string { 203 if ht, ok := t.(test.HelperT); ok { 204 ht.Helper() 205 } 206 client := testEnv.APIClient() 207 pluginList, err := client.PluginList(context.Background(), filters.Args{}) 208 // Docker EE does not allow cluster-wide plugin management. 209 if dclient.IsErrNotImplemented(err) { 210 return []string{} 211 } 212 assert.NilError(t, err, "failed to list plugins") 213 214 var plugins []string 215 for _, plugin := range pluginList { 216 plugins = append(plugins, plugin.Name) 217 } 218 return plugins 219} 220 221// ProtectVolume adds the specified volume(s) to be protected in case of clean 222func (e *Execution) ProtectVolume(t assert.TestingT, volumes ...string) { 223 if ht, ok := t.(test.HelperT); ok { 224 ht.Helper() 225 } 226 for _, volume := range volumes { 227 e.protectedElements.volumes[volume] = struct{}{} 228 } 229} 230 231// ProtectVolumes protects existing volumes from being cleaned up at the end of 232// test runs 233func ProtectVolumes(t assert.TestingT, testEnv *Execution) { 234 if ht, ok := t.(test.HelperT); ok { 235 ht.Helper() 236 } 237 volumes := getExistingVolumes(t, testEnv) 238 testEnv.ProtectVolume(t, volumes...) 239} 240 241func getExistingVolumes(t assert.TestingT, testEnv *Execution) []string { 242 if ht, ok := t.(test.HelperT); ok { 243 ht.Helper() 244 } 245 client := testEnv.APIClient() 246 volumeList, err := client.VolumeList(context.Background(), filters.Args{}) 247 assert.NilError(t, err, "failed to list volumes") 248 249 var volumes []string 250 for _, volume := range volumeList.Volumes { 251 volumes = append(volumes, volume.Name) 252 } 253 return volumes 254} 255