github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/testutil/environment/clean.go (about)

     1  package environment // import "github.com/Prakhar-Agarwal-byte/moby/testutil/environment"
     2  
     3  import (
     4  	"context"
     5  	"regexp"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/Prakhar-Agarwal-byte/moby/api/types"
    10  	"github.com/Prakhar-Agarwal-byte/moby/api/types/container"
    11  	"github.com/Prakhar-Agarwal-byte/moby/api/types/filters"
    12  	"github.com/Prakhar-Agarwal-byte/moby/api/types/volume"
    13  	"github.com/Prakhar-Agarwal-byte/moby/client"
    14  	"github.com/Prakhar-Agarwal-byte/moby/errdefs"
    15  	"go.opentelemetry.io/otel"
    16  	"gotest.tools/v3/assert"
    17  )
    18  
    19  // Clean the environment, preserving protected objects (images, containers, ...)
    20  // and removing everything else. It's meant to run after any tests so that they don't
    21  // depend on each others.
    22  func (e *Execution) Clean(ctx context.Context, t testing.TB) {
    23  	t.Helper()
    24  
    25  	ctx, span := otel.Tracer("").Start(ctx, "CleanupEnvironment")
    26  	defer span.End()
    27  
    28  	apiClient := e.APIClient()
    29  
    30  	platform := e.DaemonInfo.OSType
    31  	if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") {
    32  		unpauseAllContainers(ctx, t, apiClient)
    33  	}
    34  	deleteAllContainers(ctx, t, apiClient, e.protectedElements.containers)
    35  	deleteAllImages(ctx, t, apiClient, e.protectedElements.images)
    36  	deleteAllVolumes(ctx, t, apiClient, e.protectedElements.volumes)
    37  	deleteAllNetworks(ctx, t, apiClient, platform, e.protectedElements.networks)
    38  	if platform == "linux" {
    39  		deleteAllPlugins(ctx, t, apiClient, e.protectedElements.plugins)
    40  	}
    41  }
    42  
    43  func unpauseAllContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) {
    44  	t.Helper()
    45  	containers := getPausedContainers(ctx, t, client)
    46  	if len(containers) > 0 {
    47  		for _, ctr := range containers {
    48  			err := client.ContainerUnpause(ctx, ctr.ID)
    49  			assert.Check(t, err, "failed to unpause container %s", ctr.ID)
    50  		}
    51  	}
    52  }
    53  
    54  func getPausedContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container {
    55  	t.Helper()
    56  	containers, err := client.ContainerList(ctx, container.ListOptions{
    57  		Filters: filters.NewArgs(filters.Arg("status", "paused")),
    58  		All:     true,
    59  	})
    60  	assert.Check(t, err, "failed to list containers")
    61  	return containers
    62  }
    63  
    64  var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`)
    65  
    66  func deleteAllContainers(ctx context.Context, t testing.TB, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) {
    67  	t.Helper()
    68  	containers := getAllContainers(ctx, t, apiclient)
    69  	if len(containers) == 0 {
    70  		return
    71  	}
    72  
    73  	for _, ctr := range containers {
    74  		if _, ok := protectedContainers[ctr.ID]; ok {
    75  			continue
    76  		}
    77  		err := apiclient.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{
    78  			Force:         true,
    79  			RemoveVolumes: true,
    80  		})
    81  		if err == nil || errdefs.IsNotFound(err) || alreadyExists.MatchString(err.Error()) || isErrNotFoundSwarmClassic(err) {
    82  			continue
    83  		}
    84  		assert.Check(t, err, "failed to remove %s", ctr.ID)
    85  	}
    86  }
    87  
    88  func getAllContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container {
    89  	t.Helper()
    90  	containers, err := client.ContainerList(ctx, container.ListOptions{
    91  		All: true,
    92  	})
    93  	assert.Check(t, err, "failed to list containers")
    94  	return containers
    95  }
    96  
    97  func deleteAllImages(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
    98  	t.Helper()
    99  	images, err := apiclient.ImageList(ctx, types.ImageListOptions{})
   100  	assert.Check(t, err, "failed to list images")
   101  
   102  	for _, image := range images {
   103  		tags := tagsFromImageSummary(image)
   104  		if _, ok := protectedImages[image.ID]; ok {
   105  			continue
   106  		}
   107  		if len(tags) == 0 {
   108  			removeImage(ctx, t, apiclient, image.ID)
   109  			continue
   110  		}
   111  		for _, tag := range tags {
   112  			if _, ok := protectedImages[tag]; !ok {
   113  				removeImage(ctx, t, apiclient, tag)
   114  			}
   115  		}
   116  	}
   117  }
   118  
   119  func removeImage(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, ref string) {
   120  	t.Helper()
   121  	_, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{
   122  		Force: true,
   123  	})
   124  	if errdefs.IsNotFound(err) {
   125  		return
   126  	}
   127  	assert.Check(t, err, "failed to remove image %s", ref)
   128  }
   129  
   130  func deleteAllVolumes(ctx context.Context, t testing.TB, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) {
   131  	t.Helper()
   132  	volumes, err := c.VolumeList(ctx, volume.ListOptions{})
   133  	assert.Check(t, err, "failed to list volumes")
   134  
   135  	for _, v := range volumes.Volumes {
   136  		if _, ok := protectedVolumes[v.Name]; ok {
   137  			continue
   138  		}
   139  		err := c.VolumeRemove(ctx, v.Name, true)
   140  		// Docker EE may list volumes that no longer exist.
   141  		if isErrNotFoundSwarmClassic(err) {
   142  			continue
   143  		}
   144  		assert.Check(t, err, "failed to remove volume %s", v.Name)
   145  	}
   146  }
   147  
   148  func deleteAllNetworks(ctx context.Context, t testing.TB, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) {
   149  	t.Helper()
   150  	networks, err := c.NetworkList(ctx, types.NetworkListOptions{})
   151  	assert.Check(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(ctx, n.ID)
   165  		assert.Check(t, err, "failed to remove network %s", n.ID)
   166  	}
   167  }
   168  
   169  func deleteAllPlugins(ctx context.Context, t testing.TB, c client.PluginAPIClient, protectedPlugins map[string]struct{}) {
   170  	t.Helper()
   171  	plugins, err := c.PluginList(ctx, filters.Args{})
   172  	// Docker EE does not allow cluster-wide plugin management.
   173  	if errdefs.IsNotImplemented(err) {
   174  		return
   175  	}
   176  	assert.Check(t, err, "failed to list plugins")
   177  
   178  	for _, p := range plugins {
   179  		if _, ok := protectedPlugins[p.Name]; ok {
   180  			continue
   181  		}
   182  		err := c.PluginRemove(ctx, p.Name, types.PluginRemoveOptions{Force: true})
   183  		assert.Check(t, err, "failed to remove plugin %s", p.ID)
   184  	}
   185  }
   186  
   187  // Swarm classic aggregates node errors and returns a 500 so we need to check
   188  // the error string instead of just IsErrNotFound().
   189  func isErrNotFoundSwarmClassic(err error) bool {
   190  	return err != nil && strings.Contains(strings.ToLower(err.Error()), "no such")
   191  }