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