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  }