github.com/wozhu6104/docker@v20.10.10+incompatible/integration-cli/docker_cli_rmi_test.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/docker/docker/integration-cli/cli"
    10  	"github.com/docker/docker/integration-cli/cli/build"
    11  	"github.com/docker/docker/pkg/stringid"
    12  	"gotest.tools/v3/assert"
    13  	"gotest.tools/v3/icmd"
    14  )
    15  
    16  func (s *DockerSuite) TestRmiWithContainerFails(c *testing.T) {
    17  	errSubstr := "is using it"
    18  
    19  	// create a container
    20  	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
    21  
    22  	cleanedContainerID := strings.TrimSpace(out)
    23  
    24  	// try to delete the image
    25  	out, _, err := dockerCmdWithError("rmi", "busybox")
    26  	// Container is using image, should not be able to rmi
    27  	assert.ErrorContains(c, err, "")
    28  	// Container is using image, error message should contain errSubstr
    29  	assert.Assert(c, strings.Contains(out, errSubstr), "Container: %q", cleanedContainerID)
    30  	// make sure it didn't delete the busybox name
    31  	images, _ := dockerCmd(c, "images")
    32  	// The name 'busybox' should not have been removed from images
    33  	assert.Assert(c, strings.Contains(images, "busybox"))
    34  }
    35  
    36  func (s *DockerSuite) TestRmiTag(c *testing.T) {
    37  	imagesBefore, _ := dockerCmd(c, "images", "-a")
    38  	dockerCmd(c, "tag", "busybox", "utest:tag1")
    39  	dockerCmd(c, "tag", "busybox", "utest/docker:tag2")
    40  	dockerCmd(c, "tag", "busybox", "utest:5000/docker:tag3")
    41  	{
    42  		imagesAfter, _ := dockerCmd(c, "images", "-a")
    43  		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+3, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
    44  	}
    45  	dockerCmd(c, "rmi", "utest/docker:tag2")
    46  	{
    47  		imagesAfter, _ := dockerCmd(c, "images", "-a")
    48  		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+2, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
    49  	}
    50  	dockerCmd(c, "rmi", "utest:5000/docker:tag3")
    51  	{
    52  		imagesAfter, _ := dockerCmd(c, "images", "-a")
    53  		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+1, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
    54  
    55  	}
    56  	dockerCmd(c, "rmi", "utest:tag1")
    57  	{
    58  		imagesAfter, _ := dockerCmd(c, "images", "-a")
    59  		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n"), fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
    60  
    61  	}
    62  }
    63  
    64  func (s *DockerSuite) TestRmiImgIDMultipleTag(c *testing.T) {
    65  	out := cli.DockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir '/busybox-one'").Combined()
    66  	containerID := strings.TrimSpace(out)
    67  
    68  	// Wait for it to exit as cannot commit a running container on Windows, and
    69  	// it will take a few seconds to exit
    70  	if testEnv.OSType == "windows" {
    71  		cli.WaitExited(c, containerID, 60*time.Second)
    72  	}
    73  
    74  	cli.DockerCmd(c, "commit", containerID, "busybox-one")
    75  
    76  	imagesBefore := cli.DockerCmd(c, "images", "-a").Combined()
    77  	cli.DockerCmd(c, "tag", "busybox-one", "busybox-one:tag1")
    78  	cli.DockerCmd(c, "tag", "busybox-one", "busybox-one:tag2")
    79  
    80  	imagesAfter := cli.DockerCmd(c, "images", "-a").Combined()
    81  	// tag busybox to create 2 more images with same imageID
    82  	assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+2, fmt.Sprintf("docker images shows: %q\n", imagesAfter))
    83  
    84  	imgID := inspectField(c, "busybox-one:tag1", "Id")
    85  
    86  	// run a container with the image
    87  	out = runSleepingContainerInImage(c, "busybox-one")
    88  	containerID = strings.TrimSpace(out)
    89  
    90  	// first checkout without force it fails
    91  	// rmi tagged in multiple repos should have failed without force
    92  	cli.Docker(cli.Args("rmi", imgID)).Assert(c, icmd.Expected{
    93  		ExitCode: 1,
    94  		Err:      fmt.Sprintf("conflict: unable to delete %s (cannot be forced) - image is being used by running container %s", stringid.TruncateID(imgID), stringid.TruncateID(containerID)),
    95  	})
    96  
    97  	cli.DockerCmd(c, "stop", containerID)
    98  	cli.DockerCmd(c, "rmi", "-f", imgID)
    99  
   100  	imagesAfter = cli.DockerCmd(c, "images", "-a").Combined()
   101  	// rmi -f failed, image still exists
   102  	assert.Assert(c, !strings.Contains(imagesAfter, imgID[:12]), "ImageID:%q; ImagesAfter: %q", imgID, imagesAfter)
   103  }
   104  
   105  func (s *DockerSuite) TestRmiImgIDForce(c *testing.T) {
   106  	out := cli.DockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir '/busybox-test'").Combined()
   107  	containerID := strings.TrimSpace(out)
   108  
   109  	// Wait for it to exit as cannot commit a running container on Windows, and
   110  	// it will take a few seconds to exit
   111  	if testEnv.OSType == "windows" {
   112  		cli.WaitExited(c, containerID, 60*time.Second)
   113  	}
   114  
   115  	cli.DockerCmd(c, "commit", containerID, "busybox-test")
   116  
   117  	imagesBefore := cli.DockerCmd(c, "images", "-a").Combined()
   118  	cli.DockerCmd(c, "tag", "busybox-test", "utest:tag1")
   119  	cli.DockerCmd(c, "tag", "busybox-test", "utest:tag2")
   120  	cli.DockerCmd(c, "tag", "busybox-test", "utest/docker:tag3")
   121  	cli.DockerCmd(c, "tag", "busybox-test", "utest:5000/docker:tag4")
   122  	{
   123  		imagesAfter := cli.DockerCmd(c, "images", "-a").Combined()
   124  		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+4, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
   125  	}
   126  	imgID := inspectField(c, "busybox-test", "Id")
   127  
   128  	// first checkout without force it fails
   129  	cli.Docker(cli.Args("rmi", imgID)).Assert(c, icmd.Expected{
   130  		ExitCode: 1,
   131  		Err:      "(must be forced) - image is referenced in multiple repositories",
   132  	})
   133  
   134  	cli.DockerCmd(c, "rmi", "-f", imgID)
   135  	{
   136  		imagesAfter := cli.DockerCmd(c, "images", "-a").Combined()
   137  		// rmi failed, image still exists
   138  		assert.Assert(c, !strings.Contains(imagesAfter, imgID[:12]))
   139  	}
   140  }
   141  
   142  // See https://github.com/docker/docker/issues/14116
   143  func (s *DockerSuite) TestRmiImageIDForceWithRunningContainersAndMultipleTags(c *testing.T) {
   144  	dockerfile := "FROM busybox\nRUN echo test 14116\n"
   145  	buildImageSuccessfully(c, "test-14116", build.WithDockerfile(dockerfile))
   146  	imgID := getIDByName(c, "test-14116")
   147  
   148  	newTag := "newtag"
   149  	dockerCmd(c, "tag", imgID, newTag)
   150  	runSleepingContainerInImage(c, imgID)
   151  
   152  	out, _, err := dockerCmdWithError("rmi", "-f", imgID)
   153  	// rmi -f should not delete image with running containers
   154  	assert.ErrorContains(c, err, "")
   155  	assert.Assert(c, strings.Contains(out, "(cannot be forced) - image is being used by running container"))
   156  }
   157  
   158  func (s *DockerSuite) TestRmiTagWithExistingContainers(c *testing.T) {
   159  	container := "test-delete-tag"
   160  	newtag := "busybox:newtag"
   161  	bb := "busybox:latest"
   162  	dockerCmd(c, "tag", bb, newtag)
   163  
   164  	dockerCmd(c, "run", "--name", container, bb, "/bin/true")
   165  
   166  	out, _ := dockerCmd(c, "rmi", newtag)
   167  	assert.Equal(c, strings.Count(out, "Untagged: "), 1)
   168  }
   169  
   170  func (s *DockerSuite) TestRmiForceWithExistingContainers(c *testing.T) {
   171  	image := "busybox-clone"
   172  
   173  	icmd.RunCmd(icmd.Cmd{
   174  		Command: []string{dockerBinary, "build", "--no-cache", "-t", image, "-"},
   175  		Stdin: strings.NewReader(`FROM busybox
   176  MAINTAINER foo`),
   177  	}).Assert(c, icmd.Success)
   178  
   179  	dockerCmd(c, "run", "--name", "test-force-rmi", image, "/bin/true")
   180  
   181  	dockerCmd(c, "rmi", "-f", image)
   182  }
   183  
   184  func (s *DockerSuite) TestRmiWithMultipleRepositories(c *testing.T) {
   185  	newRepo := "127.0.0.1:5000/busybox"
   186  	oldRepo := "busybox"
   187  	newTag := "busybox:test"
   188  	dockerCmd(c, "tag", oldRepo, newRepo)
   189  
   190  	dockerCmd(c, "run", "--name", "test", oldRepo, "touch", "/abcd")
   191  
   192  	dockerCmd(c, "commit", "test", newTag)
   193  
   194  	out, _ := dockerCmd(c, "rmi", newTag)
   195  	assert.Assert(c, strings.Contains(out, "Untagged: "+newTag))
   196  }
   197  
   198  func (s *DockerSuite) TestRmiForceWithMultipleRepositories(c *testing.T) {
   199  	imageName := "rmiimage"
   200  	tag1 := imageName + ":tag1"
   201  	tag2 := imageName + ":tag2"
   202  
   203  	buildImageSuccessfully(c, tag1, build.WithDockerfile(`FROM busybox
   204  		MAINTAINER "docker"`))
   205  	dockerCmd(c, "tag", tag1, tag2)
   206  
   207  	out, _ := dockerCmd(c, "rmi", "-f", tag2)
   208  	assert.Assert(c, strings.Contains(out, "Untagged: "+tag2))
   209  	assert.Assert(c, !strings.Contains(out, "Untagged: "+tag1))
   210  	// Check built image still exists
   211  	images, _ := dockerCmd(c, "images", "-a")
   212  	assert.Assert(c, strings.Contains(images, imageName), "Built image missing %q; Images: %q", imageName, images)
   213  }
   214  
   215  func (s *DockerSuite) TestRmiBlank(c *testing.T) {
   216  	out, _, err := dockerCmdWithError("rmi", " ")
   217  	// Should have failed to delete ' ' image
   218  	assert.ErrorContains(c, err, "")
   219  	// Wrong error message generated
   220  	assert.Assert(c, !strings.Contains(out, "no such id"), "out: %s", out)
   221  	// Expected error message not generated
   222  	assert.Assert(c, strings.Contains(out, "image name cannot be blank"), "out: %s", out)
   223  }
   224  
   225  func (s *DockerSuite) TestRmiContainerImageNotFound(c *testing.T) {
   226  	// Build 2 images for testing.
   227  	imageNames := []string{"test1", "test2"}
   228  	imageIds := make([]string, 2)
   229  	for i, name := range imageNames {
   230  		dockerfile := fmt.Sprintf("FROM busybox\nMAINTAINER %s\nRUN echo %s\n", name, name)
   231  		buildImageSuccessfully(c, name, build.WithoutCache, build.WithDockerfile(dockerfile))
   232  		id := getIDByName(c, name)
   233  		imageIds[i] = id
   234  	}
   235  
   236  	// Create a long-running container.
   237  	runSleepingContainerInImage(c, imageNames[0])
   238  
   239  	// Create a stopped container, and then force remove its image.
   240  	dockerCmd(c, "run", imageNames[1], "true")
   241  	dockerCmd(c, "rmi", "-f", imageIds[1])
   242  
   243  	// Try to remove the image of the running container and see if it fails as expected.
   244  	out, _, err := dockerCmdWithError("rmi", "-f", imageIds[0])
   245  	// The image of the running container should not be removed.
   246  	assert.ErrorContains(c, err, "")
   247  	assert.Assert(c, strings.Contains(out, "image is being used by running container"), "out: %s", out)
   248  }
   249  
   250  // #13422
   251  func (s *DockerSuite) TestRmiUntagHistoryLayer(c *testing.T) {
   252  	image := "tmp1"
   253  	// Build an image for testing.
   254  	dockerfile := `FROM busybox
   255  MAINTAINER foo
   256  RUN echo 0 #layer0
   257  RUN echo 1 #layer1
   258  RUN echo 2 #layer2
   259  `
   260  	buildImageSuccessfully(c, image, build.WithoutCache, build.WithDockerfile(dockerfile))
   261  	out, _ := dockerCmd(c, "history", "-q", image)
   262  	ids := strings.Split(out, "\n")
   263  	idToTag := ids[2]
   264  
   265  	// Tag layer0 to "tmp2".
   266  	newTag := "tmp2"
   267  	dockerCmd(c, "tag", idToTag, newTag)
   268  	// Create a container based on "tmp1".
   269  	dockerCmd(c, "run", "-d", image, "true")
   270  
   271  	// See if the "tmp2" can be untagged.
   272  	out, _ = dockerCmd(c, "rmi", newTag)
   273  	// Expected 1 untagged entry
   274  	assert.Equal(c, strings.Count(out, "Untagged: "), 1, fmt.Sprintf("out: %s", out))
   275  
   276  	// Now let's add the tag again and create a container based on it.
   277  	dockerCmd(c, "tag", idToTag, newTag)
   278  	out, _ = dockerCmd(c, "run", "-d", newTag, "true")
   279  	cid := strings.TrimSpace(out)
   280  
   281  	// At this point we have 2 containers, one based on layer2 and another based on layer0.
   282  	// Try to untag "tmp2" without the -f flag.
   283  	out, _, err := dockerCmdWithError("rmi", newTag)
   284  	// should not be untagged without the -f flag
   285  	assert.ErrorContains(c, err, "")
   286  	assert.Assert(c, strings.Contains(out, cid[:12]))
   287  	assert.Assert(c, strings.Contains(out, "(must force)"))
   288  	// Add the -f flag and test again.
   289  	out, _ = dockerCmd(c, "rmi", "-f", newTag)
   290  	// should be allowed to untag with the -f flag
   291  	assert.Assert(c, strings.Contains(out, fmt.Sprintf("Untagged: %s:latest", newTag)))
   292  }
   293  
   294  func (*DockerSuite) TestRmiParentImageFail(c *testing.T) {
   295  	buildImageSuccessfully(c, "test", build.WithDockerfile(`
   296  	FROM busybox
   297  	RUN echo hello`))
   298  
   299  	id := inspectField(c, "busybox", "ID")
   300  	out, _, err := dockerCmdWithError("rmi", id)
   301  	assert.ErrorContains(c, err, "")
   302  	if !strings.Contains(out, "image has dependent child images") {
   303  		c.Fatalf("rmi should have failed because it's a parent image, got %s", out)
   304  	}
   305  }
   306  
   307  func (s *DockerSuite) TestRmiWithParentInUse(c *testing.T) {
   308  	out, _ := dockerCmd(c, "create", "busybox")
   309  	cID := strings.TrimSpace(out)
   310  
   311  	out, _ = dockerCmd(c, "commit", cID)
   312  	imageID := strings.TrimSpace(out)
   313  
   314  	out, _ = dockerCmd(c, "create", imageID)
   315  	cID = strings.TrimSpace(out)
   316  
   317  	out, _ = dockerCmd(c, "commit", cID)
   318  	imageID = strings.TrimSpace(out)
   319  
   320  	dockerCmd(c, "rmi", imageID)
   321  }
   322  
   323  // #18873
   324  func (s *DockerSuite) TestRmiByIDHardConflict(c *testing.T) {
   325  	dockerCmd(c, "create", "busybox")
   326  
   327  	imgID := inspectField(c, "busybox:latest", "Id")
   328  
   329  	_, _, err := dockerCmdWithError("rmi", imgID[:12])
   330  	assert.ErrorContains(c, err, "")
   331  
   332  	// check that tag was not removed
   333  	imgID2 := inspectField(c, "busybox:latest", "Id")
   334  	assert.Equal(c, imgID, imgID2)
   335  }