Rather than using 2 different functions for different
types of conflicts use a bitmask to specify what
conflicts need to be checked. This allows a better way
to make exceptions.
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
| ... | ... |
@@ -12,6 +12,17 @@ import ( |
| 12 | 12 |
"github.com/docker/engine-api/types" |
| 13 | 13 |
) |
| 14 | 14 |
|
| 15 |
+type conflictType int |
|
| 16 |
+ |
|
| 17 |
+const ( |
|
| 18 |
+ conflictDependentChild conflictType = (1 << iota) |
|
| 19 |
+ conflictRunningContainer |
|
| 20 |
+ conflictActiveReference |
|
| 21 |
+ conflictStoppedContainer |
|
| 22 |
+ conflictHard = conflictDependentChild | conflictRunningContainer |
|
| 23 |
+ conflictSoft = conflictActiveReference | conflictStoppedContainer |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 15 | 26 |
// ImageDelete deletes the image referenced by the given imageRef from this |
| 16 | 27 |
// daemon. The given imageRef can be an image ID, ID prefix, or a repository |
| 17 | 28 |
// 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 |
| 128 | 128 |
// remove that reference. |
| 129 | 129 |
// FIXME: Is this the behavior we want? |
| 130 | 130 |
if len(repoRefs) == 1 {
|
| 131 |
- if conflict := daemon.checkImageDeleteConflict(imgID, force, true); conflict != nil {
|
|
| 131 |
+ c := conflictHard |
|
| 132 |
+ if !force {
|
|
| 133 |
+ c |= conflictSoft &^ conflictActiveReference |
|
| 134 |
+ } |
|
| 135 |
+ if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil {
|
|
| 132 | 136 |
return nil, conflict |
| 133 | 137 |
} |
| 134 | 138 |
|
| ... | ... |
@@ -245,7 +260,11 @@ func (idc *imageDeleteConflict) Error() string {
|
| 245 | 245 |
func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDelete, force, prune, quiet bool) error {
|
| 246 | 246 |
// First, determine if this image has any conflicts. Ignore soft conflicts |
| 247 | 247 |
// if force is true. |
| 248 |
- if conflict := daemon.checkImageDeleteConflict(imgID, force, false); conflict != nil {
|
|
| 248 |
+ c := conflictHard |
|
| 249 |
+ if !force {
|
|
| 250 |
+ c |= conflictSoft |
|
| 251 |
+ } |
|
| 252 |
+ if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil {
|
|
| 249 | 253 |
if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) {
|
| 250 | 254 |
// Ignore conflicts UNLESS the image is "dangling" or not being used in |
| 251 | 255 |
// which case we want the user to know. |
| ... | ... |
@@ -297,24 +316,9 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe |
| 297 | 297 |
// using the image. A soft conflict is any tags/digest referencing the given |
| 298 | 298 |
// image or any stopped container using the image. If ignoreSoftConflicts is |
| 299 | 299 |
// true, this function will not check for soft conflict conditions. |
| 300 |
-func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflicts bool, ignoreRefConflict bool) *imageDeleteConflict {
|
|
| 301 |
- // Check for hard conflicts first. |
|
| 302 |
- if conflict := daemon.checkImageDeleteHardConflict(imgID); conflict != nil {
|
|
| 303 |
- return conflict |
|
| 304 |
- } |
|
| 305 |
- |
|
| 306 |
- // Then check for soft conflicts. |
|
| 307 |
- if ignoreSoftConflicts {
|
|
| 308 |
- // Don't bother checking for soft conflicts. |
|
| 309 |
- return nil |
|
| 310 |
- } |
|
| 311 |
- |
|
| 312 |
- return daemon.checkImageDeleteSoftConflict(imgID, ignoreRefConflict) |
|
| 313 |
-} |
|
| 314 |
- |
|
| 315 |
-func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteConflict {
|
|
| 300 |
+func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType) *imageDeleteConflict {
|
|
| 316 | 301 |
// Check if the image has any descendent images. |
| 317 |
- if len(daemon.imageStore.Children(imgID)) > 0 {
|
|
| 302 |
+ if mask&conflictDependentChild != 0 && len(daemon.imageStore.Children(imgID)) > 0 {
|
|
| 318 | 303 |
return &imageDeleteConflict{
|
| 319 | 304 |
hard: true, |
| 320 | 305 |
imgID: imgID, |
| ... | ... |
@@ -322,47 +326,47 @@ func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteC |
| 322 | 322 |
} |
| 323 | 323 |
} |
| 324 | 324 |
|
| 325 |
- // Check if any running container is using the image. |
|
| 326 |
- for _, container := range daemon.List() {
|
|
| 327 |
- if !container.IsRunning() {
|
|
| 328 |
- // Skip this until we check for soft conflicts later. |
|
| 329 |
- continue |
|
| 330 |
- } |
|
| 325 |
+ if mask&conflictRunningContainer != 0 {
|
|
| 326 |
+ // Check if any running container is using the image. |
|
| 327 |
+ for _, container := range daemon.List() {
|
|
| 328 |
+ if !container.IsRunning() {
|
|
| 329 |
+ // Skip this until we check for soft conflicts later. |
|
| 330 |
+ continue |
|
| 331 |
+ } |
|
| 331 | 332 |
|
| 332 |
- if container.ImageID == imgID {
|
|
| 333 |
- return &imageDeleteConflict{
|
|
| 334 |
- imgID: imgID, |
|
| 335 |
- hard: true, |
|
| 336 |
- used: true, |
|
| 337 |
- message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
|
|
| 333 |
+ if container.ImageID == imgID {
|
|
| 334 |
+ return &imageDeleteConflict{
|
|
| 335 |
+ imgID: imgID, |
|
| 336 |
+ hard: true, |
|
| 337 |
+ used: true, |
|
| 338 |
+ message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
|
|
| 339 |
+ } |
|
| 338 | 340 |
} |
| 339 | 341 |
} |
| 340 | 342 |
} |
| 341 | 343 |
|
| 342 |
- return nil |
|
| 343 |
-} |
|
| 344 |
- |
|
| 345 |
-func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID, ignoreRefConflict bool) *imageDeleteConflict {
|
|
| 346 | 344 |
// Check if any repository tags/digest reference this image. |
| 347 |
- if !ignoreRefConflict && len(daemon.referenceStore.References(imgID)) > 0 {
|
|
| 345 |
+ if mask&conflictActiveReference != 0 && len(daemon.referenceStore.References(imgID)) > 0 {
|
|
| 348 | 346 |
return &imageDeleteConflict{
|
| 349 | 347 |
imgID: imgID, |
| 350 | 348 |
message: "image is referenced in one or more repositories", |
| 351 | 349 |
} |
| 352 | 350 |
} |
| 353 | 351 |
|
| 354 |
- // Check if any stopped containers reference this image. |
|
| 355 |
- for _, container := range daemon.List() {
|
|
| 356 |
- if container.IsRunning() {
|
|
| 357 |
- // Skip this as it was checked above in hard conflict conditions. |
|
| 358 |
- continue |
|
| 359 |
- } |
|
| 352 |
+ if mask&conflictStoppedContainer != 0 {
|
|
| 353 |
+ // Check if any stopped containers reference this image. |
|
| 354 |
+ for _, container := range daemon.List() {
|
|
| 355 |
+ if container.IsRunning() {
|
|
| 356 |
+ // Skip this as it was checked above in hard conflict conditions. |
|
| 357 |
+ continue |
|
| 358 |
+ } |
|
| 360 | 359 |
|
| 361 |
- if container.ImageID == imgID {
|
|
| 362 |
- return &imageDeleteConflict{
|
|
| 363 |
- imgID: imgID, |
|
| 364 |
- used: true, |
|
| 365 |
- message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
|
|
| 360 |
+ if container.ImageID == imgID {
|
|
| 361 |
+ return &imageDeleteConflict{
|
|
| 362 |
+ imgID: imgID, |
|
| 363 |
+ used: true, |
|
| 364 |
+ message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
|
|
| 365 |
+ } |
|
| 366 | 366 |
} |
| 367 | 367 |
} |
| 368 | 368 |
} |