Prechádzať zdrojové kódy

Allow protection of entire environment during tests

Signed-off-by: Christopher Crone <christopher.crone@docker.com>
Christopher Crone 7 rokov pred
rodič
commit
0520581523

+ 20 - 8
internal/test/environment/clean.go

@@ -32,12 +32,12 @@ func (e *Execution) Clean(t testingT) {
 	if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") {
 		unpauseAllContainers(t, client)
 	}
-	deleteAllContainers(t, client)
+	deleteAllContainers(t, client, e.protectedElements.containers)
 	deleteAllImages(t, client, e.protectedElements.images)
-	deleteAllVolumes(t, client)
-	deleteAllNetworks(t, client, platform)
+	deleteAllVolumes(t, client, e.protectedElements.volumes)
+	deleteAllNetworks(t, client, platform, e.protectedElements.networks)
 	if platform == "linux" {
-		deleteAllPlugins(t, client)
+		deleteAllPlugins(t, client, e.protectedElements.plugins)
 	}
 }
 
@@ -66,7 +66,7 @@ func getPausedContainers(ctx context.Context, t assert.TestingT, client client.C
 
 var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`)
 
-func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient) {
+func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) {
 	ctx := context.Background()
 	containers := getAllContainers(ctx, t, apiclient)
 	if len(containers) == 0 {
@@ -74,6 +74,9 @@ func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient)
 	}
 
 	for _, container := range containers {
+		if _, ok := protectedContainers[container.ID]; ok {
+			continue
+		}
 		err := apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
 			Force:         true,
 			RemoveVolumes: true,
@@ -126,17 +129,20 @@ func removeImage(ctx context.Context, t assert.TestingT, apiclient client.ImageA
 	assert.NoError(t, err, "failed to remove image %s", ref)
 }
 
-func deleteAllVolumes(t assert.TestingT, c client.VolumeAPIClient) {
+func deleteAllVolumes(t assert.TestingT, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) {
 	volumes, err := c.VolumeList(context.Background(), filters.Args{})
 	assert.NoError(t, err, "failed to list volumes")
 
 	for _, v := range volumes.Volumes {
+		if _, ok := protectedVolumes[v.Name]; ok {
+			continue
+		}
 		err := c.VolumeRemove(context.Background(), v.Name, true)
 		assert.NoError(t, err, "failed to remove volume %s", v.Name)
 	}
 }
 
-func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatform string) {
+func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) {
 	networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
 	assert.NoError(t, err, "failed to list networks")
 
@@ -144,6 +150,9 @@ func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatf
 		if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
 			continue
 		}
+		if _, ok := protectedNetworks[n.ID]; ok {
+			continue
+		}
 		if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
 			// nat is a pre-defined network on Windows and cannot be removed
 			continue
@@ -153,11 +162,14 @@ func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatf
 	}
 }
 
-func deleteAllPlugins(t assert.TestingT, c client.PluginAPIClient) {
+func deleteAllPlugins(t assert.TestingT, c client.PluginAPIClient, protectedPlugins map[string]struct{}) {
 	plugins, err := c.PluginList(context.Background(), filters.Args{})
 	assert.NoError(t, err, "failed to list plugins")
 
 	for _, p := range plugins {
+		if _, ok := protectedPlugins[p.Name]; ok {
+			continue
+		}
 		err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
 		assert.NoError(t, err, "failed to remove plugin %s", p.ID)
 	}

+ 137 - 7
internal/test/environment/protect.go

@@ -10,7 +10,63 @@ import (
 )
 
 type protectedElements struct {
-	images map[string]struct{}
+	containers map[string]struct{}
+	images     map[string]struct{}
+	networks   map[string]struct{}
+	plugins    map[string]struct{}
+	volumes    map[string]struct{}
+}
+
+func newProtectedElements() protectedElements {
+	return protectedElements{
+		containers: map[string]struct{}{},
+		images:     map[string]struct{}{},
+		networks:   map[string]struct{}{},
+		plugins:    map[string]struct{}{},
+		volumes:    map[string]struct{}{},
+	}
+}
+
+// ProtectAll protects the existing environment (containers, images, networks,
+// volumes, and, on Linux, plugins) from being cleaned up at the end of test
+// runs
+func ProtectAll(t testingT, testEnv *Execution) {
+	ProtectContainers(t, testEnv)
+	ProtectImages(t, testEnv)
+	ProtectNetworks(t, testEnv)
+	ProtectVolumes(t, testEnv)
+	if testEnv.DaemonInfo.OSType == "linux" {
+		ProtectPlugins(t, testEnv)
+	}
+}
+
+// ProtectContainer adds the specified container(s) to be protected in case of
+// clean
+func (e *Execution) ProtectContainer(t testingT, containers ...string) {
+	for _, container := range containers {
+		e.protectedElements.containers[container] = struct{}{}
+	}
+}
+
+// ProtectContainers protects existing containers from being cleaned up at the
+// end of test runs
+func ProtectContainers(t testingT, testEnv *Execution) {
+	containers := getExistingContainers(t, testEnv)
+	testEnv.ProtectContainer(t, containers...)
+}
+
+func getExistingContainers(t require.TestingT, testEnv *Execution) []string {
+	client := testEnv.APIClient()
+	containerList, err := client.ContainerList(context.Background(), types.ContainerListOptions{
+		All: true,
+	})
+	require.NoError(t, err, "failed to list containers")
+
+	containers := []string{}
+	for _, container := range containerList {
+		containers = append(containers, container.ID)
+	}
+	return containers
 }
 
 // ProtectImage adds the specified image(s) to be protected in case of clean
@@ -20,12 +76,6 @@ func (e *Execution) ProtectImage(t testingT, images ...string) {
 	}
 }
 
-func newProtectedElements() protectedElements {
-	return protectedElements{
-		images: map[string]struct{}{},
-	}
-}
-
 // ProtectImages protects existing images and on linux frozen images from being
 // cleaned up at the end of test runs
 func ProtectImages(t testingT, testEnv *Execution) {
@@ -42,6 +92,7 @@ func getExistingImages(t require.TestingT, testEnv *Execution) []string {
 	filter := filters.NewArgs()
 	filter.Add("dangling", "false")
 	imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
+		All:     true,
 		Filters: filter,
 	})
 	require.NoError(t, err, "failed to list images")
@@ -76,3 +127,82 @@ func ensureFrozenImagesLinux(t testingT, testEnv *Execution) []string {
 	}
 	return images
 }
+
+// ProtectNetwork adds the specified network(s) to be protected in case of
+// clean
+func (e *Execution) ProtectNetwork(t testingT, networks ...string) {
+	for _, network := range networks {
+		e.protectedElements.networks[network] = struct{}{}
+	}
+}
+
+// ProtectNetworks protects existing networks from being cleaned up at the end
+// of test runs
+func ProtectNetworks(t testingT, testEnv *Execution) {
+	networks := getExistingNetworks(t, testEnv)
+	testEnv.ProtectNetwork(t, networks...)
+}
+
+func getExistingNetworks(t require.TestingT, testEnv *Execution) []string {
+	client := testEnv.APIClient()
+	networkList, err := client.NetworkList(context.Background(), types.NetworkListOptions{})
+	require.NoError(t, err, "failed to list networks")
+
+	networks := []string{}
+	for _, network := range networkList {
+		networks = append(networks, network.ID)
+	}
+	return networks
+}
+
+// ProtectPlugin adds the specified plugin(s) to be protected in case of clean
+func (e *Execution) ProtectPlugin(t testingT, plugins ...string) {
+	for _, plugin := range plugins {
+		e.protectedElements.plugins[plugin] = struct{}{}
+	}
+}
+
+// ProtectPlugins protects existing plugins from being cleaned up at the end of
+// test runs
+func ProtectPlugins(t testingT, testEnv *Execution) {
+	plugins := getExistingPlugins(t, testEnv)
+	testEnv.ProtectPlugin(t, plugins...)
+}
+
+func getExistingPlugins(t require.TestingT, testEnv *Execution) []string {
+	client := testEnv.APIClient()
+	pluginList, err := client.PluginList(context.Background(), filters.Args{})
+	require.NoError(t, err, "failed to list plugins")
+
+	plugins := []string{}
+	for _, plugin := range pluginList {
+		plugins = append(plugins, plugin.Name)
+	}
+	return plugins
+}
+
+// ProtectVolume adds the specified volume(s) to be protected in case of clean
+func (e *Execution) ProtectVolume(t testingT, volumes ...string) {
+	for _, volume := range volumes {
+		e.protectedElements.volumes[volume] = struct{}{}
+	}
+}
+
+// ProtectVolumes protects existing volumes from being cleaned up at the end of
+// test runs
+func ProtectVolumes(t testingT, testEnv *Execution) {
+	volumes := getExistingVolumes(t, testEnv)
+	testEnv.ProtectVolume(t, volumes...)
+}
+
+func getExistingVolumes(t require.TestingT, testEnv *Execution) []string {
+	client := testEnv.APIClient()
+	volumeList, err := client.VolumeList(context.Background(), filters.Args{})
+	require.NoError(t, err, "failed to list volumes")
+
+	volumes := []string{}
+	for _, volume := range volumeList.Volumes {
+		volumes = append(volumes, volume.Name)
+	}
+	return volumes
+}