github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/internal/test/environment/clean.go (about)

     1  package environment
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  
     7  	"github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/api/types/filters"
     9  	"github.com/docker/docker/client"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	"golang.org/x/net/context"
    13  )
    14  
    15  type testingT interface {
    16  	require.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 testingT) {
    29  	client := e.APIClient()
    30  
    31  	platform := e.OSType
    32  	if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") {
    33  		unpauseAllContainers(t, client)
    34  	}
    35  	deleteAllContainers(t, client, e.protectedElements.containers)
    36  	deleteAllImages(t, client, e.protectedElements.images)
    37  	deleteAllVolumes(t, client, e.protectedElements.volumes)
    38  	deleteAllNetworks(t, client, platform, e.protectedElements.networks)
    39  	if platform == "linux" {
    40  		deleteAllPlugins(t, client, e.protectedElements.plugins)
    41  	}
    42  }
    43  
    44  func unpauseAllContainers(t assert.TestingT, client client.ContainerAPIClient) {
    45  	ctx := context.Background()
    46  	containers := getPausedContainers(ctx, t, client)
    47  	if len(containers) > 0 {
    48  		for _, container := range containers {
    49  			err := client.ContainerUnpause(ctx, container.ID)
    50  			assert.NoError(t, err, "failed to unpause container %s", container.ID)
    51  		}
    52  	}
    53  }
    54  
    55  func getPausedContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container {
    56  	filter := filters.NewArgs()
    57  	filter.Add("status", "paused")
    58  	containers, err := client.ContainerList(ctx, types.ContainerListOptions{
    59  		Filters: filter,
    60  		Quiet:   true,
    61  		All:     true,
    62  	})
    63  	assert.NoError(t, err, "failed to list containers")
    64  	return containers
    65  }
    66  
    67  var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`)
    68  
    69  func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) {
    70  	ctx := context.Background()
    71  	containers := getAllContainers(ctx, t, apiclient)
    72  	if len(containers) == 0 {
    73  		return
    74  	}
    75  
    76  	for _, container := range containers {
    77  		if _, ok := protectedContainers[container.ID]; ok {
    78  			continue
    79  		}
    80  		err := apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
    81  			Force:         true,
    82  			RemoveVolumes: true,
    83  		})
    84  		if err == nil || client.IsErrNotFound(err) || alreadyExists.MatchString(err.Error()) || isErrNotFoundSwarmClassic(err) {
    85  			continue
    86  		}
    87  		assert.NoError(t, err, "failed to remove %s", container.ID)
    88  	}
    89  }
    90  
    91  func getAllContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container {
    92  	containers, err := client.ContainerList(ctx, types.ContainerListOptions{
    93  		Quiet: true,
    94  		All:   true,
    95  	})
    96  	assert.NoError(t, err, "failed to list containers")
    97  	return containers
    98  }
    99  
   100  func deleteAllImages(t testingT, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
   101  	images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{})
   102  	assert.NoError(t, err, "failed to list images")
   103  
   104  	ctx := context.Background()
   105  	for _, image := range images {
   106  		tags := tagsFromImageSummary(image)
   107  		if len(tags) == 0 {
   108  			t.Logf("Removing image %s", image.ID)
   109  			removeImage(ctx, t, apiclient, image.ID)
   110  			continue
   111  		}
   112  		for _, tag := range tags {
   113  			if _, ok := protectedImages[tag]; !ok {
   114  				t.Logf("Removing image %s", tag)
   115  				removeImage(ctx, t, apiclient, tag)
   116  				continue
   117  			}
   118  		}
   119  	}
   120  }
   121  
   122  func removeImage(ctx context.Context, t assert.TestingT, apiclient client.ImageAPIClient, ref string) {
   123  	_, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{
   124  		Force: true,
   125  	})
   126  	if client.IsErrNotFound(err) {
   127  		return
   128  	}
   129  	assert.NoError(t, err, "failed to remove image %s", ref)
   130  }
   131  
   132  func deleteAllVolumes(t assert.TestingT, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) {
   133  	volumes, err := c.VolumeList(context.Background(), filters.Args{})
   134  	assert.NoError(t, err, "failed to list volumes")
   135  
   136  	for _, v := range volumes.Volumes {
   137  		if _, ok := protectedVolumes[v.Name]; ok {
   138  			continue
   139  		}
   140  		err := c.VolumeRemove(context.Background(), v.Name, true)
   141  		// Docker EE may list volumes that no longer exist.
   142  		if isErrNotFoundSwarmClassic(err) {
   143  			continue
   144  		}
   145  		assert.NoError(t, err, "failed to remove volume %s", v.Name)
   146  	}
   147  }
   148  
   149  func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) {
   150  	networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
   151  	assert.NoError(t, err, "failed to list networks")
   152  
   153  	for _, n := range networks {
   154  		if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
   155  			continue
   156  		}
   157  		if _, ok := protectedNetworks[n.ID]; ok {
   158  			continue
   159  		}
   160  		if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
   161  			// nat is a pre-defined network on Windows and cannot be removed
   162  			continue
   163  		}
   164  		err := c.NetworkRemove(context.Background(), n.ID)
   165  		assert.NoError(t, err, "failed to remove network %s", n.ID)
   166  	}
   167  }
   168  
   169  func deleteAllPlugins(t assert.TestingT, c client.PluginAPIClient, protectedPlugins map[string]struct{}) {
   170  	plugins, err := c.PluginList(context.Background(), filters.Args{})
   171  	// Docker EE does not allow cluster-wide plugin management.
   172  	if client.IsErrNotImplemented(err) {
   173  		return
   174  	}
   175  	assert.NoError(t, err, "failed to list plugins")
   176  
   177  	for _, p := range plugins {
   178  		if _, ok := protectedPlugins[p.Name]; ok {
   179  			continue
   180  		}
   181  		err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
   182  		assert.NoError(t, err, "failed to remove plugin %s", p.ID)
   183  	}
   184  }
   185  
   186  // Swarm classic aggregates node errors and returns a 500 so we need to check
   187  // the error string instead of just IsErrNotFound().
   188  func isErrNotFoundSwarmClassic(err error) bool {
   189  	return err != nil && strings.Contains(strings.ToLower(err.Error()), "no such")
   190  }