mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Fix rmi by ID untagging image on error
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>
This commit is contained in:
parent
1393c450cd
commit
38a45eed88
2 changed files with 26 additions and 5 deletions
|
@ -102,6 +102,10 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
|
||||||
// remove that reference.
|
// remove that reference.
|
||||||
// FIXME: Is this the behavior we want?
|
// FIXME: Is this the behavior we want?
|
||||||
if len(repoRefs) == 1 {
|
if len(repoRefs) == 1 {
|
||||||
|
if conflict := daemon.checkImageDeleteConflict(imgID, force, true); conflict != nil {
|
||||||
|
return nil, conflict
|
||||||
|
}
|
||||||
|
|
||||||
parsedRef, err := daemon.removeImageRef(repoRefs[0])
|
parsedRef, err := daemon.removeImageRef(repoRefs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -215,7 +219,7 @@ func (idc *imageDeleteConflict) Error() string {
|
||||||
func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDelete, force, prune, quiet bool) error {
|
func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDelete, force, prune, quiet bool) error {
|
||||||
// First, determine if this image has any conflicts. Ignore soft conflicts
|
// First, determine if this image has any conflicts. Ignore soft conflicts
|
||||||
// if force is true.
|
// if force is true.
|
||||||
if conflict := daemon.checkImageDeleteConflict(imgID, force); conflict != nil {
|
if conflict := daemon.checkImageDeleteConflict(imgID, force, false); conflict != nil {
|
||||||
if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) {
|
if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) {
|
||||||
// Ignore conflicts UNLESS the image is "dangling" or not being used in
|
// Ignore conflicts UNLESS the image is "dangling" or not being used in
|
||||||
// which case we want the user to know.
|
// which case we want the user to know.
|
||||||
|
@ -267,7 +271,7 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe
|
||||||
// using the image. A soft conflict is any tags/digest referencing the given
|
// using the image. A soft conflict is any tags/digest referencing the given
|
||||||
// image or any stopped container using the image. If ignoreSoftConflicts is
|
// image or any stopped container using the image. If ignoreSoftConflicts is
|
||||||
// true, this function will not check for soft conflict conditions.
|
// true, this function will not check for soft conflict conditions.
|
||||||
func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflicts bool) *imageDeleteConflict {
|
func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflicts bool, ignoreRefConflict bool) *imageDeleteConflict {
|
||||||
// Check for hard conflicts first.
|
// Check for hard conflicts first.
|
||||||
if conflict := daemon.checkImageDeleteHardConflict(imgID); conflict != nil {
|
if conflict := daemon.checkImageDeleteHardConflict(imgID); conflict != nil {
|
||||||
return conflict
|
return conflict
|
||||||
|
@ -279,7 +283,7 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflic
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return daemon.checkImageDeleteSoftConflict(imgID)
|
return daemon.checkImageDeleteSoftConflict(imgID, ignoreRefConflict)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteConflict {
|
func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteConflict {
|
||||||
|
@ -312,9 +316,9 @@ func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteC
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteConflict {
|
func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID, ignoreRefConflict bool) *imageDeleteConflict {
|
||||||
// Check if any repository tags/digest reference this image.
|
// Check if any repository tags/digest reference this image.
|
||||||
if len(daemon.referenceStore.References(imgID)) > 0 {
|
if !ignoreRefConflict && len(daemon.referenceStore.References(imgID)) > 0 {
|
||||||
return &imageDeleteConflict{
|
return &imageDeleteConflict{
|
||||||
imgID: imgID,
|
imgID: imgID,
|
||||||
message: "image is referenced in one or more repositories",
|
message: "image is referenced in one or more repositories",
|
||||||
|
|
|
@ -337,3 +337,20 @@ func (s *DockerSuite) TestRmiWithParentInUse(c *check.C) {
|
||||||
|
|
||||||
dockerCmd(c, "rmi", imageID)
|
dockerCmd(c, "rmi", imageID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #18873
|
||||||
|
func (s *DockerSuite) TestRmiByIDHardConflict(c *check.C) {
|
||||||
|
testRequires(c, DaemonIsLinux)
|
||||||
|
dockerCmd(c, "create", "busybox")
|
||||||
|
|
||||||
|
imgID, err := inspectField("busybox:latest", "Id")
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
_, _, err = dockerCmdWithError("rmi", imgID[:12])
|
||||||
|
c.Assert(err, checker.NotNil)
|
||||||
|
|
||||||
|
// check that tag was not removed
|
||||||
|
imgID2, err := inspectField("busybox:latest", "Id")
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(imgID, checker.Equals, imgID2)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue