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