Merge pull request #19275 from tonistiigi/delete-image-conflict-bitmask
Use bitmask for conflict checking
This commit is contained in:
commit
40d7ad98a8
|
@ -12,6 +12,17 @@ import (
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type conflictType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
conflictDependentChild conflictType = (1 << iota)
|
||||||
|
conflictRunningContainer
|
||||||
|
conflictActiveReference
|
||||||
|
conflictStoppedContainer
|
||||||
|
conflictHard = conflictDependentChild | conflictRunningContainer
|
||||||
|
conflictSoft = conflictActiveReference | conflictStoppedContainer
|
||||||
|
)
|
||||||
|
|
||||||
// ImageDelete deletes the image referenced by the given imageRef from this
|
// ImageDelete deletes the image referenced by the given imageRef from this
|
||||||
// daemon. The given imageRef can be an image ID, ID prefix, or a repository
|
// daemon. The given imageRef can be an image ID, ID prefix, or a repository
|
||||||
// reference (with an optional tag or digest, defaulting to the tag name
|
// reference (with an optional tag or digest, defaulting to the tag name
|
||||||
|
@ -128,7 +139,11 @@ 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 {
|
c := conflictHard
|
||||||
|
if !force {
|
||||||
|
c |= conflictSoft &^ conflictActiveReference
|
||||||
|
}
|
||||||
|
if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil {
|
||||||
return nil, conflict
|
return nil, conflict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +260,11 @@ 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, false); conflict != nil {
|
c := conflictHard
|
||||||
|
if !force {
|
||||||
|
c |= conflictSoft
|
||||||
|
}
|
||||||
|
if conflict := daemon.checkImageDeleteConflict(imgID, c); 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.
|
||||||
|
@ -297,24 +316,9 @@ 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, ignoreRefConflict bool) *imageDeleteConflict {
|
func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType) *imageDeleteConflict {
|
||||||
// Check for hard conflicts first.
|
|
||||||
if conflict := daemon.checkImageDeleteHardConflict(imgID); conflict != nil {
|
|
||||||
return conflict
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check for soft conflicts.
|
|
||||||
if ignoreSoftConflicts {
|
|
||||||
// Don't bother checking for soft conflicts.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return daemon.checkImageDeleteSoftConflict(imgID, ignoreRefConflict)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteConflict {
|
|
||||||
// Check if the image has any descendent images.
|
// Check if the image has any descendent images.
|
||||||
if len(daemon.imageStore.Children(imgID)) > 0 {
|
if mask&conflictDependentChild != 0 && len(daemon.imageStore.Children(imgID)) > 0 {
|
||||||
return &imageDeleteConflict{
|
return &imageDeleteConflict{
|
||||||
hard: true,
|
hard: true,
|
||||||
imgID: imgID,
|
imgID: imgID,
|
||||||
|
@ -322,47 +326,47 @@ func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any running container is using the image.
|
if mask&conflictRunningContainer != 0 {
|
||||||
for _, container := range daemon.List() {
|
// Check if any running container is using the image.
|
||||||
if !container.IsRunning() {
|
for _, container := range daemon.List() {
|
||||||
// Skip this until we check for soft conflicts later.
|
if !container.IsRunning() {
|
||||||
continue
|
// Skip this until we check for soft conflicts later.
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if container.ImageID == imgID {
|
if container.ImageID == imgID {
|
||||||
return &imageDeleteConflict{
|
return &imageDeleteConflict{
|
||||||
imgID: imgID,
|
imgID: imgID,
|
||||||
hard: true,
|
hard: true,
|
||||||
used: true,
|
used: true,
|
||||||
message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
|
message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 !ignoreRefConflict && len(daemon.referenceStore.References(imgID)) > 0 {
|
if mask&conflictActiveReference != 0 && 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",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any stopped containers reference this image.
|
if mask&conflictStoppedContainer != 0 {
|
||||||
for _, container := range daemon.List() {
|
// Check if any stopped containers reference this image.
|
||||||
if container.IsRunning() {
|
for _, container := range daemon.List() {
|
||||||
// Skip this as it was checked above in hard conflict conditions.
|
if container.IsRunning() {
|
||||||
continue
|
// Skip this as it was checked above in hard conflict conditions.
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if container.ImageID == imgID {
|
if container.ImageID == imgID {
|
||||||
return &imageDeleteConflict{
|
return &imageDeleteConflict{
|
||||||
imgID: imgID,
|
imgID: imgID,
|
||||||
used: true,
|
used: true,
|
||||||
message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
|
message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue