github.com/rish1988/moby@v25.0.2+incompatible/testutil/environment/protect.go (about) 1 package environment 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/docker/docker/api/types" 8 "github.com/docker/docker/api/types/container" 9 "github.com/docker/docker/api/types/filters" 10 "github.com/docker/docker/api/types/image" 11 "github.com/docker/docker/api/types/volume" 12 "github.com/docker/docker/errdefs" 13 "github.com/docker/docker/testutil" 14 "go.opentelemetry.io/otel" 15 "gotest.tools/v3/assert" 16 ) 17 18 var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:bookworm-slim"} 19 20 type protectedElements struct { 21 containers map[string]struct{} 22 images map[string]struct{} 23 networks map[string]struct{} 24 plugins map[string]struct{} 25 volumes map[string]struct{} 26 } 27 28 func newProtectedElements() protectedElements { 29 return protectedElements{ 30 containers: map[string]struct{}{}, 31 images: map[string]struct{}{}, 32 networks: map[string]struct{}{}, 33 plugins: map[string]struct{}{}, 34 volumes: map[string]struct{}{}, 35 } 36 } 37 38 // ProtectAll protects the existing environment (containers, images, networks, 39 // volumes, and, on Linux, plugins) from being cleaned up at the end of test 40 // runs 41 func ProtectAll(ctx context.Context, t testing.TB, testEnv *Execution) { 42 testutil.CheckNotParallel(t) 43 44 t.Helper() 45 ctx, span := otel.Tracer("").Start(ctx, "ProtectAll") 46 defer span.End() 47 48 ProtectContainers(ctx, t, testEnv) 49 ProtectImages(ctx, t, testEnv) 50 ProtectNetworks(ctx, t, testEnv) 51 ProtectVolumes(ctx, t, testEnv) 52 if testEnv.DaemonInfo.OSType == "linux" { 53 ProtectPlugins(ctx, t, testEnv) 54 } 55 } 56 57 // ProtectContainer adds the specified container(s) to be protected in case of 58 // clean 59 func (e *Execution) ProtectContainer(t testing.TB, containers ...string) { 60 t.Helper() 61 for _, container := range containers { 62 e.protectedElements.containers[container] = struct{}{} 63 } 64 } 65 66 // ProtectContainers protects existing containers from being cleaned up at the 67 // end of test runs 68 func ProtectContainers(ctx context.Context, t testing.TB, testEnv *Execution) { 69 t.Helper() 70 containers := getExistingContainers(ctx, t, testEnv) 71 testEnv.ProtectContainer(t, containers...) 72 } 73 74 func getExistingContainers(ctx context.Context, t testing.TB, testEnv *Execution) []string { 75 t.Helper() 76 client := testEnv.APIClient() 77 containerList, err := client.ContainerList(ctx, container.ListOptions{ 78 All: true, 79 }) 80 assert.NilError(t, err, "failed to list containers") 81 82 var containers []string 83 for _, container := range containerList { 84 containers = append(containers, container.ID) 85 } 86 return containers 87 } 88 89 // ProtectImage adds the specified image(s) to be protected in case of clean 90 func (e *Execution) ProtectImage(t testing.TB, images ...string) { 91 t.Helper() 92 for _, img := range images { 93 e.protectedElements.images[img] = 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 99 func ProtectImages(ctx context.Context, t testing.TB, testEnv *Execution) { 100 t.Helper() 101 images := getExistingImages(ctx, t, testEnv) 102 103 if testEnv.DaemonInfo.OSType == "linux" { 104 images = append(images, frozenImages...) 105 } 106 testEnv.ProtectImage(t, images...) 107 } 108 109 func getExistingImages(ctx context.Context, t testing.TB, testEnv *Execution) []string { 110 t.Helper() 111 client := testEnv.APIClient() 112 imageList, err := client.ImageList(ctx, image.ListOptions{ 113 All: true, 114 Filters: filters.NewArgs(filters.Arg("dangling", "false")), 115 }) 116 assert.NilError(t, err, "failed to list images") 117 118 var images []string 119 for _, img := range imageList { 120 images = append(images, tagsFromImageSummary(img)...) 121 } 122 return images 123 } 124 125 func tagsFromImageSummary(image image.Summary) []string { 126 var result []string 127 for _, tag := range image.RepoTags { 128 // Starting from API 1.43 no longer outputs the hardcoded <none> 129 // strings. But since the tests might be ran against a remote 130 // daemon/pre 1.43 CLI we must still be able to handle it. 131 if tag != "<none>:<none>" { 132 result = append(result, tag) 133 } 134 } 135 for _, digest := range image.RepoDigests { 136 if digest != "<none>@<none>" { 137 result = append(result, digest) 138 } 139 } 140 return result 141 } 142 143 // ProtectNetwork adds the specified network(s) to be protected in case of 144 // clean 145 func (e *Execution) ProtectNetwork(t testing.TB, networks ...string) { 146 t.Helper() 147 for _, network := range networks { 148 e.protectedElements.networks[network] = struct{}{} 149 } 150 } 151 152 // ProtectNetworks protects existing networks from being cleaned up at the end 153 // of test runs 154 func ProtectNetworks(ctx context.Context, t testing.TB, testEnv *Execution) { 155 t.Helper() 156 networks := getExistingNetworks(ctx, t, testEnv) 157 testEnv.ProtectNetwork(t, networks...) 158 } 159 160 func getExistingNetworks(ctx context.Context, t testing.TB, testEnv *Execution) []string { 161 t.Helper() 162 client := testEnv.APIClient() 163 networkList, err := client.NetworkList(ctx, types.NetworkListOptions{}) 164 assert.NilError(t, err, "failed to list networks") 165 166 var networks []string 167 for _, network := range networkList { 168 networks = append(networks, network.ID) 169 } 170 return networks 171 } 172 173 // ProtectPlugin adds the specified plugin(s) to be protected in case of clean 174 func (e *Execution) ProtectPlugin(t testing.TB, plugins ...string) { 175 t.Helper() 176 for _, plugin := range plugins { 177 e.protectedElements.plugins[plugin] = struct{}{} 178 } 179 } 180 181 // ProtectPlugins protects existing plugins from being cleaned up at the end of 182 // test runs 183 func ProtectPlugins(ctx context.Context, t testing.TB, testEnv *Execution) { 184 t.Helper() 185 plugins := getExistingPlugins(ctx, t, testEnv) 186 testEnv.ProtectPlugin(t, plugins...) 187 } 188 189 func getExistingPlugins(ctx context.Context, t testing.TB, testEnv *Execution) []string { 190 t.Helper() 191 client := testEnv.APIClient() 192 pluginList, err := client.PluginList(ctx, filters.Args{}) 193 // Docker EE does not allow cluster-wide plugin management. 194 if errdefs.IsNotImplemented(err) { 195 return []string{} 196 } 197 assert.NilError(t, err, "failed to list plugins") 198 199 var plugins []string 200 for _, plugin := range pluginList { 201 plugins = append(plugins, plugin.Name) 202 } 203 return plugins 204 } 205 206 // ProtectVolume adds the specified volume(s) to be protected in case of clean 207 func (e *Execution) ProtectVolume(t testing.TB, volumes ...string) { 208 t.Helper() 209 for _, vol := range volumes { 210 e.protectedElements.volumes[vol] = struct{}{} 211 } 212 } 213 214 // ProtectVolumes protects existing volumes from being cleaned up at the end of 215 // test runs 216 func ProtectVolumes(ctx context.Context, t testing.TB, testEnv *Execution) { 217 t.Helper() 218 volumes := getExistingVolumes(ctx, t, testEnv) 219 testEnv.ProtectVolume(t, volumes...) 220 } 221 222 func getExistingVolumes(ctx context.Context, t testing.TB, testEnv *Execution) []string { 223 t.Helper() 224 client := testEnv.APIClient() 225 volumeList, err := client.VolumeList(ctx, volume.ListOptions{}) 226 assert.NilError(t, err, "failed to list volumes") 227 228 var volumes []string 229 for _, vol := range volumeList.Volumes { 230 volumes = append(volumes, vol.Name) 231 } 232 return volumes 233 }