Signed-off-by: Lei Jitang <leijitang@huawei.com>
| ... | ... |
@@ -195,6 +195,7 @@ func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]ty |
| 195 | 195 |
// Implements the error interface. |
| 196 | 196 |
type imageDeleteConflict struct {
|
| 197 | 197 |
hard bool |
| 198 |
+ used bool |
|
| 198 | 199 |
imgID image.ID |
| 199 | 200 |
message string |
| 200 | 201 |
} |
| ... | ... |
@@ -225,8 +226,8 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe |
| 225 | 225 |
// First, determine if this image has any conflicts. Ignore soft conflicts |
| 226 | 226 |
// if force is true. |
| 227 | 227 |
if conflict := daemon.checkImageDeleteConflict(imgID, force); conflict != nil {
|
| 228 |
- if quiet && !daemon.imageIsDangling(imgID) {
|
|
| 229 |
- // Ignore conflicts UNLESS the image is "dangling" in |
|
| 228 |
+ if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) {
|
|
| 229 |
+ // Ignore conflicts UNLESS the image is "dangling" or not being used in |
|
| 230 | 230 |
// which case we want the user to know. |
| 231 | 231 |
return nil |
| 232 | 232 |
} |
| ... | ... |
@@ -312,6 +313,7 @@ func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteC |
| 312 | 312 |
return &imageDeleteConflict{
|
| 313 | 313 |
imgID: imgID, |
| 314 | 314 |
hard: true, |
| 315 |
+ used: true, |
|
| 315 | 316 |
message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
|
| 316 | 317 |
} |
| 317 | 318 |
} |
| ... | ... |
@@ -339,6 +341,7 @@ func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteC |
| 339 | 339 |
if container.ImageID == imgID {
|
| 340 | 340 |
return &imageDeleteConflict{
|
| 341 | 341 |
imgID: imgID, |
| 342 |
+ used: true, |
|
| 342 | 343 |
message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
|
| 343 | 344 |
} |
| 344 | 345 |
} |
| ... | ... |
@@ -60,9 +60,9 @@ func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) {
|
| 60 | 60 |
// rebuild dockerfile with a small addition at the end |
| 61 | 61 |
_, err = buildImage(img, dockerfile2, true) |
| 62 | 62 |
c.Assert(err, check.IsNil, check.Commentf("Could not rebuild image %s", img))
|
| 63 |
- // try to remove the image, should error out. |
|
| 63 |
+ // try to remove the image, should not error out. |
|
| 64 | 64 |
out, _, err := dockerCmdWithError("rmi", img)
|
| 65 |
- c.Assert(err, check.NotNil, check.Commentf("Expected to error out removing the image, but succeeded: %s", out))
|
|
| 65 |
+ c.Assert(err, check.IsNil, check.Commentf("Expected to removing the image, but failed: %s", out))
|
|
| 66 | 66 |
|
| 67 | 67 |
// check if we deleted the first image |
| 68 | 68 |
out, _ = dockerCmd(c, "images", "-q", "--no-trunc") |
| ... | ... |
@@ -322,3 +322,18 @@ func (*DockerSuite) TestRmiParentImageFail(c *check.C) {
|
| 322 | 322 |
c.Fatalf("rmi should have failed because it's a parent image, got %s", out)
|
| 323 | 323 |
} |
| 324 | 324 |
} |
| 325 |
+ |
|
| 326 |
+func (s *DockerSuite) TestRmiWithParentInUse(c *check.C) {
|
|
| 327 |
+ testRequires(c, DaemonIsLinux) |
|
| 328 |
+ out, _ := dockerCmd(c, "create", "busybox") |
|
| 329 |
+ cID := strings.TrimSpace(out) |
|
| 330 |
+ out, _ = dockerCmd(c, "commit", cID) |
|
| 331 |
+ imageID := strings.TrimSpace(out) |
|
| 332 |
+ |
|
| 333 |
+ out, _ = dockerCmd(c, "create", imageID) |
|
| 334 |
+ cID = strings.TrimSpace(out) |
|
| 335 |
+ out, _ = dockerCmd(c, "commit", cID) |
|
| 336 |
+ imageID = strings.TrimSpace(out) |
|
| 337 |
+ |
|
| 338 |
+ dockerCmd(c, "rmi", imageID) |
|
| 339 |
+} |