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  }