diff --git a/daemon/image_delete.go b/daemon/image_delete.go index 3847be1ca3..e2e8dd1513 100644 --- a/daemon/image_delete.go +++ b/daemon/image_delete.go @@ -195,6 +195,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]ty // Implements the error interface. type imageDeleteConflict struct { hard bool + used bool imgID image.ID message string } @@ -225,8 +226,8 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe // First, determine if this image has any conflicts. Ignore soft conflicts // if force is true. if conflict := daemon.checkImageDeleteConflict(imgID, force); conflict != nil { - if quiet && !daemon.imageIsDangling(imgID) { - // Ignore conflicts UNLESS the image is "dangling" in + if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) { + // Ignore conflicts UNLESS the image is "dangling" or not being used in // which case we want the user to know. return nil } @@ -312,6 +313,7 @@ func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteC return &imageDeleteConflict{ imgID: imgID, hard: true, + used: true, message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)), } } @@ -339,6 +341,7 @@ func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteC if container.ImageID == imgID { return &imageDeleteConflict{ imgID: imgID, + used: true, message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)), } } diff --git a/integration-cli/docker_cli_rm_test.go b/integration-cli/docker_cli_rm_test.go index a3c50673d6..554506b796 100644 --- a/integration-cli/docker_cli_rm_test.go +++ b/integration-cli/docker_cli_rm_test.go @@ -60,9 +60,9 @@ func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) { // rebuild dockerfile with a small addition at the end _, err = buildImage(img, dockerfile2, true) c.Assert(err, check.IsNil, check.Commentf("Could not rebuild image %s", img)) - // try to remove the image, should error out. + // try to remove the image, should not error out. out, _, err := dockerCmdWithError("rmi", img) - c.Assert(err, check.NotNil, check.Commentf("Expected to error out removing the image, but succeeded: %s", out)) + c.Assert(err, check.IsNil, check.Commentf("Expected to removing the image, but failed: %s", out)) // check if we deleted the first image out, _ = dockerCmd(c, "images", "-q", "--no-trunc") diff --git a/integration-cli/docker_cli_rmi_test.go b/integration-cli/docker_cli_rmi_test.go index 13fa08ef64..2f456e83a7 100644 --- a/integration-cli/docker_cli_rmi_test.go +++ b/integration-cli/docker_cli_rmi_test.go @@ -322,3 +322,18 @@ func (*DockerSuite) TestRmiParentImageFail(c *check.C) { c.Fatalf("rmi should have failed because it's a parent image, got %s", out) } } + +func (s *DockerSuite) TestRmiWithParentInUse(c *check.C) { + testRequires(c, DaemonIsLinux) + out, _ := dockerCmd(c, "create", "busybox") + cID := strings.TrimSpace(out) + out, _ = dockerCmd(c, "commit", cID) + imageID := strings.TrimSpace(out) + + out, _ = dockerCmd(c, "create", imageID) + cID = strings.TrimSpace(out) + out, _ = dockerCmd(c, "commit", cID) + imageID = strings.TrimSpace(out) + + dockerCmd(c, "rmi", imageID) +}