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