Do not untag image if it would later get a hard conflict because of running containers.
Fixes #18873
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -102,6 +102,10 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I |
| 102 | 102 |
// remove that reference. |
| 103 | 103 |
// FIXME: Is this the behavior we want? |
| 104 | 104 |
if len(repoRefs) == 1 {
|
| 105 |
+ if conflict := daemon.checkImageDeleteConflict(imgID, force, true); conflict != nil {
|
|
| 106 |
+ return nil, conflict |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 105 | 109 |
parsedRef, err := daemon.removeImageRef(repoRefs[0]) |
| 106 | 110 |
if err != nil {
|
| 107 | 111 |
return nil, err |
| ... | ... |
@@ -215,7 +219,7 @@ func (idc *imageDeleteConflict) Error() string {
|
| 215 | 215 |
func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDelete, force, prune, quiet bool) error {
|
| 216 | 216 |
// First, determine if this image has any conflicts. Ignore soft conflicts |
| 217 | 217 |
// if force is true. |
| 218 |
- if conflict := daemon.checkImageDeleteConflict(imgID, force); conflict != nil {
|
|
| 218 |
+ if conflict := daemon.checkImageDeleteConflict(imgID, force, false); conflict != nil {
|
|
| 219 | 219 |
if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) {
|
| 220 | 220 |
// Ignore conflicts UNLESS the image is "dangling" or not being used in |
| 221 | 221 |
// which case we want the user to know. |
| ... | ... |
@@ -267,7 +271,7 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe |
| 267 | 267 |
// using the image. A soft conflict is any tags/digest referencing the given |
| 268 | 268 |
// image or any stopped container using the image. If ignoreSoftConflicts is |
| 269 | 269 |
// true, this function will not check for soft conflict conditions. |
| 270 |
-func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflicts bool) *imageDeleteConflict {
|
|
| 270 |
+func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflicts bool, ignoreRefConflict bool) *imageDeleteConflict {
|
|
| 271 | 271 |
// Check for hard conflicts first. |
| 272 | 272 |
if conflict := daemon.checkImageDeleteHardConflict(imgID); conflict != nil {
|
| 273 | 273 |
return conflict |
| ... | ... |
@@ -279,7 +283,7 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflic |
| 279 | 279 |
return nil |
| 280 | 280 |
} |
| 281 | 281 |
|
| 282 |
- return daemon.checkImageDeleteSoftConflict(imgID) |
|
| 282 |
+ return daemon.checkImageDeleteSoftConflict(imgID, ignoreRefConflict) |
|
| 283 | 283 |
} |
| 284 | 284 |
|
| 285 | 285 |
func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteConflict {
|
| ... | ... |
@@ -312,9 +316,9 @@ func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteC |
| 312 | 312 |
return nil |
| 313 | 313 |
} |
| 314 | 314 |
|
| 315 |
-func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteConflict {
|
|
| 315 |
+func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID, ignoreRefConflict bool) *imageDeleteConflict {
|
|
| 316 | 316 |
// Check if any repository tags/digest reference this image. |
| 317 |
- if len(daemon.referenceStore.References(imgID)) > 0 {
|
|
| 317 |
+ if !ignoreRefConflict && len(daemon.referenceStore.References(imgID)) > 0 {
|
|
| 318 | 318 |
return &imageDeleteConflict{
|
| 319 | 319 |
imgID: imgID, |
| 320 | 320 |
message: "image is referenced in one or more repositories", |
| ... | ... |
@@ -337,3 +337,20 @@ func (s *DockerSuite) TestRmiWithParentInUse(c *check.C) {
|
| 337 | 337 |
|
| 338 | 338 |
dockerCmd(c, "rmi", imageID) |
| 339 | 339 |
} |
| 340 |
+ |
|
| 341 |
+// #18873 |
|
| 342 |
+func (s *DockerSuite) TestRmiByIDHardConflict(c *check.C) {
|
|
| 343 |
+ testRequires(c, DaemonIsLinux) |
|
| 344 |
+ dockerCmd(c, "create", "busybox") |
|
| 345 |
+ |
|
| 346 |
+ imgID, err := inspectField("busybox:latest", "Id")
|
|
| 347 |
+ c.Assert(err, checker.IsNil) |
|
| 348 |
+ |
|
| 349 |
+ _, _, err = dockerCmdWithError("rmi", imgID[:12])
|
|
| 350 |
+ c.Assert(err, checker.NotNil) |
|
| 351 |
+ |
|
| 352 |
+ // check that tag was not removed |
|
| 353 |
+ imgID2, err := inspectField("busybox:latest", "Id")
|
|
| 354 |
+ c.Assert(err, checker.IsNil) |
|
| 355 |
+ c.Assert(imgID, checker.Equals, imgID2) |
|
| 356 |
+} |