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