2018-02-07 15:52:47 -05:00
package images // import "github.com/docker/docker/daemon/images"
2014-08-05 02:41:30 -04:00
import (
"fmt"
"strings"
2016-07-20 19:11:28 -04:00
"time"
2014-08-05 02:41:30 -04:00
2017-01-25 19:54:18 -05:00
"github.com/docker/distribution/reference"
2016-09-06 14:18:12 -04:00
"github.com/docker/docker/api/types"
2015-11-12 14:55:17 -05:00
"github.com/docker/docker/container"
2018-01-11 14:53:06 -05:00
"github.com/docker/docker/errdefs"
2015-07-20 13:57:15 -04:00
"github.com/docker/docker/image"
2015-03-24 07:25:26 -04:00
"github.com/docker/docker/pkg/stringid"
2017-11-20 11:33:20 -05:00
"github.com/docker/docker/pkg/system"
2017-07-19 10:20:13 -04:00
"github.com/pkg/errors"
2014-08-05 02:41:30 -04:00
)
2016-01-12 18:03:08 -05:00
type conflictType int
const (
conflictDependentChild conflictType = ( 1 << iota )
conflictRunningContainer
conflictActiveReference
conflictStoppedContainer
conflictHard = conflictDependentChild | conflictRunningContainer
conflictSoft = conflictActiveReference | conflictStoppedContainer
)
2015-08-15 03:30:25 -04:00
// 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
// reference (with an optional tag or digest, defaulting to the tag name
// "latest"). There is differing behavior depending on whether the given
// imageRef is a repository reference or not.
//
// If the given imageRef is a repository reference then that repository
// reference will be removed. However, if there exists any containers which
// were created using the same image reference then the repository reference
// cannot be removed unless either there are other repository references to the
// same image or force is true. Following removal of the repository reference,
// the referenced image itself will attempt to be deleted as described below
// but quietly, meaning any image delete conflicts will cause the image to not
// be deleted and the conflict will not be reported.
//
// There may be conflicts preventing deletion of an image and these conflicts
// are divided into two categories grouped by their severity:
//
// Hard Conflict:
// - a pull or build using the image.
2016-02-11 18:21:52 -05:00
// - any descendant image.
2015-08-15 03:30:25 -04:00
// - any running container using the image.
//
// Soft Conflict:
// - any stopped container using the image.
// - any repository tag or digest references to the image.
//
// The image cannot be removed if there are any hard conflicts and can be
// removed if there are soft conflicts only if force is true.
//
// If prune is true, ancestor images will each attempt to be deleted quietly,
// meaning any delete conflicts will cause the image to not be deleted and the
// conflict will not be reported.
//
2018-02-07 15:52:47 -05:00
func ( i * ImageService ) ImageDelete ( imageRef string , force , prune bool ) ( [ ] types . ImageDeleteResponseItem , error ) {
2016-07-20 19:11:28 -04:00
start := time . Now ( )
2016-11-10 11:27:56 -05:00
records := [ ] types . ImageDeleteResponseItem { }
2015-08-15 03:30:25 -04:00
2018-02-14 15:19:37 -05:00
img , err := i . GetImage ( imageRef )
2015-08-15 03:30:25 -04:00
if err != nil {
2017-07-19 10:20:13 -04:00
return nil , err
2014-08-05 02:41:30 -04:00
}
2018-02-14 15:19:37 -05:00
if ! system . IsOSSupported ( img . OperatingSystem ( ) ) {
2017-11-20 11:33:20 -05:00
return nil , errors . Errorf ( "unable to delete image: %q" , system . ErrNotSupportedOperatingSystem )
}
2015-08-15 03:30:25 -04:00
2018-02-14 15:19:37 -05:00
imgID := img . ID ( )
2018-02-02 17:18:46 -05:00
repoRefs := i . referenceStore . References ( imgID . Digest ( ) )
using := func ( c * container . Container ) bool {
return c . ImageID == imgID
}
2015-11-18 17:20:54 -05:00
2015-08-15 03:30:25 -04:00
var removedRepositoryRef bool
2015-11-18 17:20:54 -05:00
if ! isImageIDPrefix ( imgID . String ( ) , imageRef ) {
2015-08-15 03:30:25 -04:00
// A repository reference was given and should be removed
// first. We can only remove this reference if either force is
// true, there are multiple repository references to this
// image, or there are no containers using the given reference.
2016-07-26 03:45:39 -04:00
if ! force && isSingleReference ( repoRefs ) {
2018-02-02 17:18:46 -05:00
if container := i . containers . First ( using ) ; container != nil {
2015-08-15 03:30:25 -04:00
// If we removed the repository reference then
// this image would remain "dangling" and since
// we really want to avoid that the client must
// explicitly force its removal.
2017-07-19 10:20:13 -04:00
err := errors . Errorf ( "conflict: unable to remove repository reference %q (must force) - container %s is using its referenced image %s" , imageRef , stringid . TruncateID ( container . ID ) , stringid . TruncateID ( imgID . String ( ) ) )
2017-11-28 23:09:37 -05:00
return nil , errdefs . Conflict ( err )
2015-08-15 03:30:25 -04:00
}
}
2017-01-25 19:54:18 -05:00
parsedRef , err := reference . ParseNormalizedNamed ( imageRef )
2015-08-15 03:30:25 -04:00
if err != nil {
return nil , err
}
2018-02-02 17:18:46 -05:00
parsedRef , err = i . removeImageRef ( parsedRef )
2015-11-18 17:20:54 -05:00
if err != nil {
return nil , err
}
2017-01-25 19:54:18 -05:00
untaggedRecord := types . ImageDeleteResponseItem { Untagged : reference . FamiliarString ( parsedRef ) }
2015-08-15 03:30:25 -04:00
2018-02-02 17:18:46 -05:00
i . LogImageEvent ( imgID . String ( ) , imgID . String ( ) , "untag" )
2015-08-15 03:30:25 -04:00
records = append ( records , untaggedRecord )
2018-02-02 17:18:46 -05:00
repoRefs = i . referenceStore . References ( imgID . Digest ( ) )
2016-01-06 20:57:21 -05:00
2016-06-16 19:06:47 -04:00
// If a tag reference was removed and the only remaining
// references to the same repository are digest references,
// then clean up those digest references.
2016-01-06 20:57:21 -05:00
if _ , isCanonical := parsedRef . ( reference . Canonical ) ; ! isCanonical {
2016-06-16 19:06:47 -04:00
foundRepoTagRef := false
2016-01-06 20:57:21 -05:00
for _ , repoRef := range repoRefs {
2016-06-16 19:06:47 -04:00
if _ , repoRefIsCanonical := repoRef . ( reference . Canonical ) ; ! repoRefIsCanonical && parsedRef . Name ( ) == repoRef . Name ( ) {
foundRepoTagRef = true
2016-01-06 20:57:21 -05:00
break
}
}
2016-06-16 19:06:47 -04:00
if ! foundRepoTagRef {
// Remove canonical references from same repository
remainingRefs := [ ] reference . Named { }
2016-01-06 20:57:21 -05:00
for _ , repoRef := range repoRefs {
2016-06-16 19:06:47 -04:00
if _ , repoRefIsCanonical := repoRef . ( reference . Canonical ) ; repoRefIsCanonical && parsedRef . Name ( ) == repoRef . Name ( ) {
2018-02-02 17:18:46 -05:00
if _ , err := i . removeImageRef ( repoRef ) ; err != nil {
2016-06-16 19:06:47 -04:00
return records , err
}
2017-01-25 19:54:18 -05:00
untaggedRecord := types . ImageDeleteResponseItem { Untagged : reference . FamiliarString ( repoRef ) }
2016-06-16 19:06:47 -04:00
records = append ( records , untaggedRecord )
} else {
remainingRefs = append ( remainingRefs , repoRef )
2016-01-06 20:57:21 -05:00
2016-06-16 19:06:47 -04:00
}
2016-01-06 20:57:21 -05:00
}
2016-06-16 19:06:47 -04:00
repoRefs = remainingRefs
2016-01-06 20:57:21 -05:00
}
}
// If it has remaining references then the untag finished the remove
if len ( repoRefs ) > 0 {
2015-10-28 19:07:02 -04:00
return records , nil
}
2015-08-15 03:30:25 -04:00
removedRepositoryRef = true
} else {
2016-06-16 19:06:47 -04:00
// If an ID reference was given AND there is at most one tag
// reference to the image AND all references are within one
// repository, then remove all references.
if isSingleReference ( repoRefs ) {
2016-01-12 18:03:08 -05:00
c := conflictHard
if ! force {
c |= conflictSoft &^ conflictActiveReference
}
2018-02-02 17:18:46 -05:00
if conflict := i . checkImageDeleteConflict ( imgID , c ) ; conflict != nil {
2016-01-12 13:55:34 -05:00
return nil , conflict
}
2016-06-16 19:06:47 -04:00
for _ , repoRef := range repoRefs {
2018-02-02 17:18:46 -05:00
parsedRef , err := i . removeImageRef ( repoRef )
2016-06-16 19:06:47 -04:00
if err != nil {
return nil , err
}
2015-08-15 03:30:25 -04:00
2017-01-25 19:54:18 -05:00
untaggedRecord := types . ImageDeleteResponseItem { Untagged : reference . FamiliarString ( parsedRef ) }
2015-08-15 03:30:25 -04:00
2018-02-02 17:18:46 -05:00
i . LogImageEvent ( imgID . String ( ) , imgID . String ( ) , "untag" )
2016-06-16 19:06:47 -04:00
records = append ( records , untaggedRecord )
}
2015-08-15 03:30:25 -04:00
}
2014-08-05 02:41:30 -04:00
}
2015-04-09 07:59:50 -04:00
2018-02-02 17:18:46 -05:00
if err := i . imageDeleteHelper ( imgID , & records , force , prune , removedRepositoryRef ) ; err != nil {
2016-07-20 19:11:28 -04:00
return nil , err
}
imageActions . WithValues ( "delete" ) . UpdateSince ( start )
return records , nil
2014-08-05 02:41:30 -04:00
}
2016-06-16 19:06:47 -04:00
// isSingleReference returns true when all references are from one repository
// and there is at most one tag. Returns false for empty input.
func isSingleReference ( repoRefs [ ] reference . Named ) bool {
if len ( repoRefs ) <= 1 {
return len ( repoRefs ) == 1
}
var singleRef reference . Named
canonicalRefs := map [ string ] struct { } { }
for _ , repoRef := range repoRefs {
if _ , isCanonical := repoRef . ( reference . Canonical ) ; isCanonical {
canonicalRefs [ repoRef . Name ( ) ] = struct { } { }
} else if singleRef == nil {
singleRef = repoRef
} else {
return false
}
}
if singleRef == nil {
// Just use first canonical ref
singleRef = repoRefs [ 0 ]
}
_ , ok := canonicalRefs [ singleRef . Name ( ) ]
return len ( canonicalRefs ) == 1 && ok
}
2015-08-15 03:30:25 -04:00
// isImageIDPrefix returns whether the given possiblePrefix is a prefix of the
// given imageID.
func isImageIDPrefix ( imageID , possiblePrefix string ) bool {
2015-11-18 17:20:54 -05:00
if strings . HasPrefix ( imageID , possiblePrefix ) {
return true
}
if i := strings . IndexRune ( imageID , ':' ) ; i >= 0 {
return strings . HasPrefix ( imageID [ i + 1 : ] , possiblePrefix )
}
2015-08-15 03:30:25 -04:00
2015-11-18 17:20:54 -05:00
return false
2015-08-15 03:30:25 -04:00
}
// removeImageRef attempts to parse and remove the given image reference from
// this daemon's store of repository tag/digest references. The given
// repositoryRef must not be an image ID but a repository name followed by an
// optional tag or digest reference. If tag or digest is omitted, the default
// tag is used. Returns the resolved image reference and an error.
2018-02-07 15:52:47 -05:00
func ( i * ImageService ) removeImageRef ( ref reference . Named ) ( reference . Named , error ) {
2017-01-25 19:54:18 -05:00
ref = reference . TagNameOnly ( ref )
2015-08-15 03:30:25 -04:00
// Ignore the boolean value returned, as far as we're concerned, this
// is an idempotent operation and it's okay if the reference didn't
// exist in the first place.
2018-02-02 17:18:46 -05:00
_ , err := i . referenceStore . Delete ( ref )
2015-08-15 03:30:25 -04:00
2015-11-18 17:20:54 -05:00
return ref , err
2015-08-15 03:30:25 -04:00
}
// removeAllReferencesToImageID attempts to remove every reference to the given
// imgID from this daemon's store of repository tag/digest references. Returns
// on the first encountered error. Removed references are logged to this
2016-11-10 11:27:56 -05:00
// daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the
2015-08-15 03:30:25 -04:00
// given list of records.
2018-02-07 15:52:47 -05:00
func ( i * ImageService ) removeAllReferencesToImageID ( imgID image . ID , records * [ ] types . ImageDeleteResponseItem ) error {
2018-02-02 17:18:46 -05:00
imageRefs := i . referenceStore . References ( imgID . Digest ( ) )
2015-08-15 03:30:25 -04:00
for _ , imageRef := range imageRefs {
2018-02-02 17:18:46 -05:00
parsedRef , err := i . removeImageRef ( imageRef )
2015-08-15 03:30:25 -04:00
if err != nil {
return err
2014-08-05 02:41:30 -04:00
}
2015-08-15 03:30:25 -04:00
2017-01-25 19:54:18 -05:00
untaggedRecord := types . ImageDeleteResponseItem { Untagged : reference . FamiliarString ( parsedRef ) }
2015-08-15 03:30:25 -04:00
2018-02-02 17:18:46 -05:00
i . LogImageEvent ( imgID . String ( ) , imgID . String ( ) , "untag" )
2015-08-15 03:30:25 -04:00
* records = append ( * records , untaggedRecord )
}
return nil
}
// ImageDeleteConflict holds a soft or hard conflict and an associated error.
// Implements the error interface.
type imageDeleteConflict struct {
hard bool
2015-11-25 20:57:20 -05:00
used bool
2015-11-18 17:20:54 -05:00
imgID image . ID
2015-08-15 03:30:25 -04:00
message string
}
func ( idc * imageDeleteConflict ) Error ( ) string {
var forceMsg string
if idc . hard {
forceMsg = "cannot be forced"
2014-08-05 02:41:30 -04:00
} else {
2015-08-15 03:30:25 -04:00
forceMsg = "must be forced"
}
2015-11-18 17:20:54 -05:00
return fmt . Sprintf ( "conflict: unable to delete %s (%s) - %s" , stringid . TruncateID ( idc . imgID . String ( ) ) , forceMsg , idc . message )
2015-08-15 03:30:25 -04:00
}
2017-07-19 10:20:13 -04:00
func ( idc * imageDeleteConflict ) Conflict ( ) { }
2015-08-15 03:30:25 -04:00
// imageDeleteHelper attempts to delete the given image from this daemon. If
// the image has any hard delete conflicts (child images or running containers
// using the image) then it cannot be deleted. If the image has any soft delete
// conflicts (any tags/digests referencing the image or any stopped container
// using the image) then it can only be deleted if force is true. If the delete
// succeeds and prune is true, the parent images are also deleted if they do
// not have any soft or hard delete conflicts themselves. Any deleted images
// and untagged references are appended to the given records. If any error or
// conflict is encountered, it will be returned immediately without deleting
// the image. If quiet is true, any encountered conflicts will be ignored and
// the function will return nil immediately without deleting the image.
2018-02-07 15:52:47 -05:00
func ( i * ImageService ) imageDeleteHelper ( imgID image . ID , records * [ ] types . ImageDeleteResponseItem , force , prune , quiet bool ) error {
2015-08-15 03:30:25 -04:00
// First, determine if this image has any conflicts. Ignore soft conflicts
// if force is true.
2016-01-12 18:03:08 -05:00
c := conflictHard
if ! force {
c |= conflictSoft
}
2018-02-02 17:18:46 -05:00
if conflict := i . checkImageDeleteConflict ( imgID , c ) ; conflict != nil {
if quiet && ( ! i . imageIsDangling ( imgID ) || conflict . used ) {
2015-11-25 20:57:20 -05:00
// Ignore conflicts UNLESS the image is "dangling" or not being used in
2015-08-15 03:30:25 -04:00
// which case we want the user to know.
return nil
}
// There was a conflict and it's either a hard conflict OR we are not
// forcing deletion on soft conflicts.
return conflict
}
2018-02-02 17:18:46 -05:00
parent , err := i . imageStore . GetParent ( imgID )
2015-11-18 17:20:54 -05:00
if err != nil {
// There may be no parent
parent = ""
}
2015-08-15 03:30:25 -04:00
// Delete all repository tag/digest references to this image.
2018-02-02 17:18:46 -05:00
if err := i . removeAllReferencesToImageID ( imgID , records ) ; err != nil {
2015-08-15 03:30:25 -04:00
return err
}
2018-02-02 17:18:46 -05:00
removedLayers , err := i . imageStore . Delete ( imgID )
2015-11-18 17:20:54 -05:00
if err != nil {
2015-08-15 03:30:25 -04:00
return err
2014-08-05 02:41:30 -04:00
}
2018-02-02 17:18:46 -05:00
i . LogImageEvent ( imgID . String ( ) , imgID . String ( ) , "delete" )
2016-11-10 11:27:56 -05:00
* records = append ( * records , types . ImageDeleteResponseItem { Deleted : imgID . String ( ) } )
2015-11-18 17:20:54 -05:00
for _ , removedLayer := range removedLayers {
2016-11-10 11:27:56 -05:00
* records = append ( * records , types . ImageDeleteResponseItem { Deleted : removedLayer . ChainID . String ( ) } )
2015-11-18 17:20:54 -05:00
}
2015-08-15 03:30:25 -04:00
2015-11-18 17:20:54 -05:00
if ! prune || parent == "" {
2014-08-05 02:41:30 -04:00
return nil
}
2015-08-15 03:30:25 -04:00
// We need to prune the parent image. This means delete it if there are
// no tags/digests referencing it and there are no containers using it (
// either running or stopped).
// Do not force prunings, but do so quietly (stopping on any encountered
// conflicts).
2018-02-02 17:18:46 -05:00
return i . imageDeleteHelper ( parent , records , false , true , true )
2015-08-15 03:30:25 -04:00
}
// checkImageDeleteConflict determines whether there are any conflicts
// preventing deletion of the given image from this daemon. A hard conflict is
// any image which has the given image as a parent or any running container
// using the image. A soft conflict is any tags/digest referencing the given
// image or any stopped container using the image. If ignoreSoftConflicts is
// true, this function will not check for soft conflict conditions.
2018-02-07 15:52:47 -05:00
func ( i * ImageService ) checkImageDeleteConflict ( imgID image . ID , mask conflictType ) * imageDeleteConflict {
2016-02-11 18:21:52 -05:00
// Check if the image has any descendant images.
2018-02-02 17:18:46 -05:00
if mask & conflictDependentChild != 0 && len ( i . imageStore . Children ( imgID ) ) > 0 {
2015-08-15 03:30:25 -04:00
return & imageDeleteConflict {
hard : true ,
2015-11-18 17:20:54 -05:00
imgID : imgID ,
2015-08-15 03:30:25 -04:00
message : "image has dependent child images" ,
2014-08-05 02:41:30 -04:00
}
}
2016-01-12 18:03:08 -05:00
if mask & conflictRunningContainer != 0 {
// Check if any running container is using the image.
2016-01-15 18:55:46 -05:00
running := func ( c * container . Container ) bool {
return c . IsRunning ( ) && c . ImageID == imgID
}
2018-02-02 17:18:46 -05:00
if container := i . containers . First ( running ) ; container != nil {
2016-01-15 18:55:46 -05:00
return & imageDeleteConflict {
imgID : imgID ,
hard : true ,
used : true ,
message : fmt . Sprintf ( "image is being used by running container %s" , stringid . TruncateID ( container . ID ) ) ,
2015-08-15 03:30:25 -04:00
}
2014-08-05 02:41:30 -04:00
}
}
2015-08-15 03:30:25 -04:00
// Check if any repository tags/digest reference this image.
2018-02-02 17:18:46 -05:00
if mask & conflictActiveReference != 0 && len ( i . referenceStore . References ( imgID . Digest ( ) ) ) > 0 {
2015-08-15 03:30:25 -04:00
return & imageDeleteConflict {
2015-11-18 17:20:54 -05:00
imgID : imgID ,
2016-07-18 07:14:27 -04:00
message : "image is referenced in multiple repositories" ,
2015-08-15 03:30:25 -04:00
}
2015-04-07 22:29:29 -04:00
}
2015-08-15 03:30:25 -04:00
2016-01-12 18:03:08 -05:00
if mask & conflictStoppedContainer != 0 {
// Check if any stopped containers reference this image.
2016-01-15 18:55:46 -05:00
stopped := func ( c * container . Container ) bool {
return ! c . IsRunning ( ) && c . ImageID == imgID
}
2018-02-02 17:18:46 -05:00
if container := i . containers . First ( stopped ) ; container != nil {
2016-01-15 18:55:46 -05:00
return & imageDeleteConflict {
imgID : imgID ,
used : true ,
message : fmt . Sprintf ( "image is being used by stopped container %s" , stringid . TruncateID ( container . ID ) ) ,
2014-08-05 02:41:30 -04:00
}
}
}
2015-08-15 03:30:25 -04:00
2014-08-05 02:41:30 -04:00
return nil
}
2015-08-15 03:30:25 -04:00
// imageIsDangling returns whether the given image is "dangling" which means
// that there are no repository references to the given image and it has no
// child images.
2018-02-07 15:52:47 -05:00
func ( i * ImageService ) imageIsDangling ( imgID image . ID ) bool {
2018-02-02 17:18:46 -05:00
return ! ( len ( i . referenceStore . References ( imgID . Digest ( ) ) ) > 0 || len ( i . imageStore . Children ( imgID ) ) > 0 )
2015-08-15 03:30:25 -04:00
}