github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/moby/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 }