github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/testutil/environment/clean.go (about) 1 package environment // import "github.com/Prakhar-Agarwal-byte/moby/testutil/environment" 2 3 import ( 4 "context" 5 "regexp" 6 "strings" 7 "testing" 8 9 "github.com/Prakhar-Agarwal-byte/moby/api/types" 10 "github.com/Prakhar-Agarwal-byte/moby/api/types/container" 11 "github.com/Prakhar-Agarwal-byte/moby/api/types/filters" 12 "github.com/Prakhar-Agarwal-byte/moby/api/types/volume" 13 "github.com/Prakhar-Agarwal-byte/moby/client" 14 "github.com/Prakhar-Agarwal-byte/moby/errdefs" 15 "go.opentelemetry.io/otel" 16 "gotest.tools/v3/assert" 17 ) 18 19 // Clean the environment, preserving protected objects (images, containers, ...) 20 // and removing everything else. It's meant to run after any tests so that they don't 21 // depend on each others. 22 func (e *Execution) Clean(ctx context.Context, t testing.TB) { 23 t.Helper() 24 25 ctx, span := otel.Tracer("").Start(ctx, "CleanupEnvironment") 26 defer span.End() 27 28 apiClient := e.APIClient() 29 30 platform := e.DaemonInfo.OSType 31 if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") { 32 unpauseAllContainers(ctx, t, apiClient) 33 } 34 deleteAllContainers(ctx, t, apiClient, e.protectedElements.containers) 35 deleteAllImages(ctx, t, apiClient, e.protectedElements.images) 36 deleteAllVolumes(ctx, t, apiClient, e.protectedElements.volumes) 37 deleteAllNetworks(ctx, t, apiClient, platform, e.protectedElements.networks) 38 if platform == "linux" { 39 deleteAllPlugins(ctx, t, apiClient, e.protectedElements.plugins) 40 } 41 } 42 43 func unpauseAllContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) { 44 t.Helper() 45 containers := getPausedContainers(ctx, t, client) 46 if len(containers) > 0 { 47 for _, ctr := range containers { 48 err := client.ContainerUnpause(ctx, ctr.ID) 49 assert.Check(t, err, "failed to unpause container %s", ctr.ID) 50 } 51 } 52 } 53 54 func getPausedContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container { 55 t.Helper() 56 containers, err := client.ContainerList(ctx, container.ListOptions{ 57 Filters: filters.NewArgs(filters.Arg("status", "paused")), 58 All: true, 59 }) 60 assert.Check(t, err, "failed to list containers") 61 return containers 62 } 63 64 var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`) 65 66 func deleteAllContainers(ctx context.Context, t testing.TB, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) { 67 t.Helper() 68 containers := getAllContainers(ctx, t, apiclient) 69 if len(containers) == 0 { 70 return 71 } 72 73 for _, ctr := range containers { 74 if _, ok := protectedContainers[ctr.ID]; ok { 75 continue 76 } 77 err := apiclient.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{ 78 Force: true, 79 RemoveVolumes: true, 80 }) 81 if err == nil || errdefs.IsNotFound(err) || alreadyExists.MatchString(err.Error()) || isErrNotFoundSwarmClassic(err) { 82 continue 83 } 84 assert.Check(t, err, "failed to remove %s", ctr.ID) 85 } 86 } 87 88 func getAllContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container { 89 t.Helper() 90 containers, err := client.ContainerList(ctx, container.ListOptions{ 91 All: true, 92 }) 93 assert.Check(t, err, "failed to list containers") 94 return containers 95 } 96 97 func deleteAllImages(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) { 98 t.Helper() 99 images, err := apiclient.ImageList(ctx, types.ImageListOptions{}) 100 assert.Check(t, err, "failed to list images") 101 102 for _, image := range images { 103 tags := tagsFromImageSummary(image) 104 if _, ok := protectedImages[image.ID]; ok { 105 continue 106 } 107 if len(tags) == 0 { 108 removeImage(ctx, t, apiclient, image.ID) 109 continue 110 } 111 for _, tag := range tags { 112 if _, ok := protectedImages[tag]; !ok { 113 removeImage(ctx, t, apiclient, tag) 114 } 115 } 116 } 117 } 118 119 func removeImage(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, ref string) { 120 t.Helper() 121 _, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{ 122 Force: true, 123 }) 124 if errdefs.IsNotFound(err) { 125 return 126 } 127 assert.Check(t, err, "failed to remove image %s", ref) 128 } 129 130 func deleteAllVolumes(ctx context.Context, t testing.TB, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) { 131 t.Helper() 132 volumes, err := c.VolumeList(ctx, volume.ListOptions{}) 133 assert.Check(t, err, "failed to list volumes") 134 135 for _, v := range volumes.Volumes { 136 if _, ok := protectedVolumes[v.Name]; ok { 137 continue 138 } 139 err := c.VolumeRemove(ctx, v.Name, true) 140 // Docker EE may list volumes that no longer exist. 141 if isErrNotFoundSwarmClassic(err) { 142 continue 143 } 144 assert.Check(t, err, "failed to remove volume %s", v.Name) 145 } 146 } 147 148 func deleteAllNetworks(ctx context.Context, t testing.TB, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) { 149 t.Helper() 150 networks, err := c.NetworkList(ctx, types.NetworkListOptions{}) 151 assert.Check(t, err, "failed to list networks") 152 153 for _, n := range networks { 154 if n.Name == "bridge" || n.Name == "none" || n.Name == "host" { 155 continue 156 } 157 if _, ok := protectedNetworks[n.ID]; ok { 158 continue 159 } 160 if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" { 161 // nat is a pre-defined network on Windows and cannot be removed 162 continue 163 } 164 err := c.NetworkRemove(ctx, n.ID) 165 assert.Check(t, err, "failed to remove network %s", n.ID) 166 } 167 } 168 169 func deleteAllPlugins(ctx context.Context, t testing.TB, c client.PluginAPIClient, protectedPlugins map[string]struct{}) { 170 t.Helper() 171 plugins, err := c.PluginList(ctx, filters.Args{}) 172 // Docker EE does not allow cluster-wide plugin management. 173 if errdefs.IsNotImplemented(err) { 174 return 175 } 176 assert.Check(t, err, "failed to list plugins") 177 178 for _, p := range plugins { 179 if _, ok := protectedPlugins[p.Name]; ok { 180 continue 181 } 182 err := c.PluginRemove(ctx, p.Name, types.PluginRemoveOptions{Force: true}) 183 assert.Check(t, err, "failed to remove plugin %s", p.ID) 184 } 185 } 186 187 // Swarm classic aggregates node errors and returns a 500 so we need to check 188 // the error string instead of just IsErrNotFound(). 189 func isErrNotFoundSwarmClassic(err error) bool { 190 return err != nil && strings.Contains(strings.ToLower(err.Error()), "no such") 191 }