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