github.com/rish1988/moby@v25.0.2+incompatible/testutil/environment/protect.go (about)

     1  package environment
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/api/types/container"
     9  	"github.com/docker/docker/api/types/filters"
    10  	"github.com/docker/docker/api/types/image"
    11  	"github.com/docker/docker/api/types/volume"
    12  	"github.com/docker/docker/errdefs"
    13  	"github.com/docker/docker/testutil"
    14  	"go.opentelemetry.io/otel"
    15  	"gotest.tools/v3/assert"
    16  )
    17  
    18  var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:bookworm-slim"}
    19  
    20  type protectedElements struct {
    21  	containers map[string]struct{}
    22  	images     map[string]struct{}
    23  	networks   map[string]struct{}
    24  	plugins    map[string]struct{}
    25  	volumes    map[string]struct{}
    26  }
    27  
    28  func newProtectedElements() protectedElements {
    29  	return protectedElements{
    30  		containers: map[string]struct{}{},
    31  		images:     map[string]struct{}{},
    32  		networks:   map[string]struct{}{},
    33  		plugins:    map[string]struct{}{},
    34  		volumes:    map[string]struct{}{},
    35  	}
    36  }
    37  
    38  // ProtectAll protects the existing environment (containers, images, networks,
    39  // volumes, and, on Linux, plugins) from being cleaned up at the end of test
    40  // runs
    41  func ProtectAll(ctx context.Context, t testing.TB, testEnv *Execution) {
    42  	testutil.CheckNotParallel(t)
    43  
    44  	t.Helper()
    45  	ctx, span := otel.Tracer("").Start(ctx, "ProtectAll")
    46  	defer span.End()
    47  
    48  	ProtectContainers(ctx, t, testEnv)
    49  	ProtectImages(ctx, t, testEnv)
    50  	ProtectNetworks(ctx, t, testEnv)
    51  	ProtectVolumes(ctx, t, testEnv)
    52  	if testEnv.DaemonInfo.OSType == "linux" {
    53  		ProtectPlugins(ctx, t, testEnv)
    54  	}
    55  }
    56  
    57  // ProtectContainer adds the specified container(s) to be protected in case of
    58  // clean
    59  func (e *Execution) ProtectContainer(t testing.TB, containers ...string) {
    60  	t.Helper()
    61  	for _, container := range containers {
    62  		e.protectedElements.containers[container] = struct{}{}
    63  	}
    64  }
    65  
    66  // ProtectContainers protects existing containers from being cleaned up at the
    67  // end of test runs
    68  func ProtectContainers(ctx context.Context, t testing.TB, testEnv *Execution) {
    69  	t.Helper()
    70  	containers := getExistingContainers(ctx, t, testEnv)
    71  	testEnv.ProtectContainer(t, containers...)
    72  }
    73  
    74  func getExistingContainers(ctx context.Context, t testing.TB, testEnv *Execution) []string {
    75  	t.Helper()
    76  	client := testEnv.APIClient()
    77  	containerList, err := client.ContainerList(ctx, container.ListOptions{
    78  		All: true,
    79  	})
    80  	assert.NilError(t, err, "failed to list containers")
    81  
    82  	var containers []string
    83  	for _, container := range containerList {
    84  		containers = append(containers, container.ID)
    85  	}
    86  	return containers
    87  }
    88  
    89  // ProtectImage adds the specified image(s) to be protected in case of clean
    90  func (e *Execution) ProtectImage(t testing.TB, images ...string) {
    91  	t.Helper()
    92  	for _, img := range images {
    93  		e.protectedElements.images[img] = struct{}{}
    94  	}
    95  }
    96  
    97  // ProtectImages protects existing images and on linux frozen images from being
    98  // cleaned up at the end of test runs
    99  func ProtectImages(ctx context.Context, t testing.TB, testEnv *Execution) {
   100  	t.Helper()
   101  	images := getExistingImages(ctx, t, testEnv)
   102  
   103  	if testEnv.DaemonInfo.OSType == "linux" {
   104  		images = append(images, frozenImages...)
   105  	}
   106  	testEnv.ProtectImage(t, images...)
   107  }
   108  
   109  func getExistingImages(ctx context.Context, t testing.TB, testEnv *Execution) []string {
   110  	t.Helper()
   111  	client := testEnv.APIClient()
   112  	imageList, err := client.ImageList(ctx, image.ListOptions{
   113  		All:     true,
   114  		Filters: filters.NewArgs(filters.Arg("dangling", "false")),
   115  	})
   116  	assert.NilError(t, err, "failed to list images")
   117  
   118  	var images []string
   119  	for _, img := range imageList {
   120  		images = append(images, tagsFromImageSummary(img)...)
   121  	}
   122  	return images
   123  }
   124  
   125  func tagsFromImageSummary(image image.Summary) []string {
   126  	var result []string
   127  	for _, tag := range image.RepoTags {
   128  		// Starting from API 1.43 no longer outputs the hardcoded <none>
   129  		// strings. But since the tests might be ran against a remote
   130  		// daemon/pre 1.43 CLI we must still be able to handle it.
   131  		if tag != "<none>:<none>" {
   132  			result = append(result, tag)
   133  		}
   134  	}
   135  	for _, digest := range image.RepoDigests {
   136  		if digest != "<none>@<none>" {
   137  			result = append(result, digest)
   138  		}
   139  	}
   140  	return result
   141  }
   142  
   143  // ProtectNetwork adds the specified network(s) to be protected in case of
   144  // clean
   145  func (e *Execution) ProtectNetwork(t testing.TB, networks ...string) {
   146  	t.Helper()
   147  	for _, network := range networks {
   148  		e.protectedElements.networks[network] = struct{}{}
   149  	}
   150  }
   151  
   152  // ProtectNetworks protects existing networks from being cleaned up at the end
   153  // of test runs
   154  func ProtectNetworks(ctx context.Context, t testing.TB, testEnv *Execution) {
   155  	t.Helper()
   156  	networks := getExistingNetworks(ctx, t, testEnv)
   157  	testEnv.ProtectNetwork(t, networks...)
   158  }
   159  
   160  func getExistingNetworks(ctx context.Context, t testing.TB, testEnv *Execution) []string {
   161  	t.Helper()
   162  	client := testEnv.APIClient()
   163  	networkList, err := client.NetworkList(ctx, types.NetworkListOptions{})
   164  	assert.NilError(t, err, "failed to list networks")
   165  
   166  	var networks []string
   167  	for _, network := range networkList {
   168  		networks = append(networks, network.ID)
   169  	}
   170  	return networks
   171  }
   172  
   173  // ProtectPlugin adds the specified plugin(s) to be protected in case of clean
   174  func (e *Execution) ProtectPlugin(t testing.TB, plugins ...string) {
   175  	t.Helper()
   176  	for _, plugin := range plugins {
   177  		e.protectedElements.plugins[plugin] = struct{}{}
   178  	}
   179  }
   180  
   181  // ProtectPlugins protects existing plugins from being cleaned up at the end of
   182  // test runs
   183  func ProtectPlugins(ctx context.Context, t testing.TB, testEnv *Execution) {
   184  	t.Helper()
   185  	plugins := getExistingPlugins(ctx, t, testEnv)
   186  	testEnv.ProtectPlugin(t, plugins...)
   187  }
   188  
   189  func getExistingPlugins(ctx context.Context, t testing.TB, testEnv *Execution) []string {
   190  	t.Helper()
   191  	client := testEnv.APIClient()
   192  	pluginList, err := client.PluginList(ctx, filters.Args{})
   193  	// Docker EE does not allow cluster-wide plugin management.
   194  	if errdefs.IsNotImplemented(err) {
   195  		return []string{}
   196  	}
   197  	assert.NilError(t, err, "failed to list plugins")
   198  
   199  	var plugins []string
   200  	for _, plugin := range pluginList {
   201  		plugins = append(plugins, plugin.Name)
   202  	}
   203  	return plugins
   204  }
   205  
   206  // ProtectVolume adds the specified volume(s) to be protected in case of clean
   207  func (e *Execution) ProtectVolume(t testing.TB, volumes ...string) {
   208  	t.Helper()
   209  	for _, vol := range volumes {
   210  		e.protectedElements.volumes[vol] = struct{}{}
   211  	}
   212  }
   213  
   214  // ProtectVolumes protects existing volumes from being cleaned up at the end of
   215  // test runs
   216  func ProtectVolumes(ctx context.Context, t testing.TB, testEnv *Execution) {
   217  	t.Helper()
   218  	volumes := getExistingVolumes(ctx, t, testEnv)
   219  	testEnv.ProtectVolume(t, volumes...)
   220  }
   221  
   222  func getExistingVolumes(ctx context.Context, t testing.TB, testEnv *Execution) []string {
   223  	t.Helper()
   224  	client := testEnv.APIClient()
   225  	volumeList, err := client.VolumeList(ctx, volume.ListOptions{})
   226  	assert.NilError(t, err, "failed to list volumes")
   227  
   228  	var volumes []string
   229  	for _, vol := range volumeList.Volumes {
   230  		volumes = append(volumes, vol.Name)
   231  	}
   232  	return volumes
   233  }