image_delete.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. package daemon
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/docker/distribution/reference"
  6. "github.com/docker/docker/api/types"
  7. derr "github.com/docker/docker/errors"
  8. "github.com/docker/docker/image"
  9. "github.com/docker/docker/pkg/stringid"
  10. tagpkg "github.com/docker/docker/tag"
  11. )
  12. // ImageDelete deletes the image referenced by the given imageRef from this
  13. // daemon. The given imageRef can be an image ID, ID prefix, or a repository
  14. // reference (with an optional tag or digest, defaulting to the tag name
  15. // "latest"). There is differing behavior depending on whether the given
  16. // imageRef is a repository reference or not.
  17. //
  18. // If the given imageRef is a repository reference then that repository
  19. // reference will be removed. However, if there exists any containers which
  20. // were created using the same image reference then the repository reference
  21. // cannot be removed unless either there are other repository references to the
  22. // same image or force is true. Following removal of the repository reference,
  23. // the referenced image itself will attempt to be deleted as described below
  24. // but quietly, meaning any image delete conflicts will cause the image to not
  25. // be deleted and the conflict will not be reported.
  26. //
  27. // There may be conflicts preventing deletion of an image and these conflicts
  28. // are divided into two categories grouped by their severity:
  29. //
  30. // Hard Conflict:
  31. // - a pull or build using the image.
  32. // - any descendent image.
  33. // - any running container using the image.
  34. //
  35. // Soft Conflict:
  36. // - any stopped container using the image.
  37. // - any repository tag or digest references to the image.
  38. //
  39. // The image cannot be removed if there are any hard conflicts and can be
  40. // removed if there are soft conflicts only if force is true.
  41. //
  42. // If prune is true, ancestor images will each attempt to be deleted quietly,
  43. // meaning any delete conflicts will cause the image to not be deleted and the
  44. // conflict will not be reported.
  45. //
  46. // FIXME: remove ImageDelete's dependency on Daemon, then move to the graph
  47. // package. This would require that we no longer need the daemon to determine
  48. // whether images are being used by a stopped or running container.
  49. func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.ImageDelete, error) {
  50. records := []types.ImageDelete{}
  51. imgID, err := daemon.GetImageID(imageRef)
  52. if err != nil {
  53. return nil, daemon.imageNotExistToErrcode(err)
  54. }
  55. repoRefs := daemon.tagStore.References(imgID)
  56. var removedRepositoryRef bool
  57. if !isImageIDPrefix(imgID.String(), imageRef) {
  58. // A repository reference was given and should be removed
  59. // first. We can only remove this reference if either force is
  60. // true, there are multiple repository references to this
  61. // image, or there are no containers using the given reference.
  62. if !(force || len(repoRefs) > 1) {
  63. if container := daemon.getContainerUsingImage(imgID); container != nil {
  64. // If we removed the repository reference then
  65. // this image would remain "dangling" and since
  66. // we really want to avoid that the client must
  67. // explicitly force its removal.
  68. return nil, derr.ErrorCodeImgDelUsed.WithArgs(imageRef, stringid.TruncateID(container.ID), stringid.TruncateID(imgID.String()))
  69. }
  70. }
  71. parsedRef, err := reference.ParseNamed(imageRef)
  72. if err != nil {
  73. return nil, err
  74. }
  75. parsedRef, err = daemon.removeImageRef(parsedRef)
  76. if err != nil {
  77. return nil, err
  78. }
  79. untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
  80. daemon.EventsService.Log("untag", imgID.String(), "")
  81. records = append(records, untaggedRecord)
  82. // If has remaining references then untag finishes the remove
  83. if len(repoRefs) > 1 {
  84. return records, nil
  85. }
  86. removedRepositoryRef = true
  87. } else {
  88. // If an ID reference was given AND there is exactly one
  89. // repository reference to the image then we will want to
  90. // remove that reference.
  91. // FIXME: Is this the behavior we want?
  92. if len(repoRefs) == 1 {
  93. parsedRef, err := daemon.removeImageRef(repoRefs[0])
  94. if err != nil {
  95. return nil, err
  96. }
  97. untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
  98. daemon.EventsService.Log("untag", imgID.String(), "")
  99. records = append(records, untaggedRecord)
  100. }
  101. }
  102. return records, daemon.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef)
  103. }
  104. // isImageIDPrefix returns whether the given possiblePrefix is a prefix of the
  105. // given imageID.
  106. func isImageIDPrefix(imageID, possiblePrefix string) bool {
  107. if strings.HasPrefix(imageID, possiblePrefix) {
  108. return true
  109. }
  110. if i := strings.IndexRune(imageID, ':'); i >= 0 {
  111. return strings.HasPrefix(imageID[i+1:], possiblePrefix)
  112. }
  113. return false
  114. }
  115. // getContainerUsingImage returns a container that was created using the given
  116. // imageID. Returns nil if there is no such container.
  117. func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *Container {
  118. for _, container := range daemon.List() {
  119. if container.ImageID == imageID {
  120. return container
  121. }
  122. }
  123. return nil
  124. }
  125. // removeImageRef attempts to parse and remove the given image reference from
  126. // this daemon's store of repository tag/digest references. The given
  127. // repositoryRef must not be an image ID but a repository name followed by an
  128. // optional tag or digest reference. If tag or digest is omitted, the default
  129. // tag is used. Returns the resolved image reference and an error.
  130. func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) {
  131. switch ref.(type) {
  132. case reference.Tagged:
  133. case reference.Digested:
  134. default:
  135. var err error
  136. ref, err = reference.WithTag(ref, tagpkg.DefaultTag)
  137. if err != nil {
  138. return nil, err
  139. }
  140. }
  141. // Ignore the boolean value returned, as far as we're concerned, this
  142. // is an idempotent operation and it's okay if the reference didn't
  143. // exist in the first place.
  144. _, err := daemon.tagStore.Delete(ref)
  145. return ref, err
  146. }
  147. // removeAllReferencesToImageID attempts to remove every reference to the given
  148. // imgID from this daemon's store of repository tag/digest references. Returns
  149. // on the first encountered error. Removed references are logged to this
  150. // daemon's event service. An "Untagged" types.ImageDelete is added to the
  151. // given list of records.
  152. func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDelete) error {
  153. imageRefs := daemon.tagStore.References(imgID)
  154. for _, imageRef := range imageRefs {
  155. parsedRef, err := daemon.removeImageRef(imageRef)
  156. if err != nil {
  157. return err
  158. }
  159. untaggedRecord := types.ImageDelete{Untagged: parsedRef.String()}
  160. daemon.EventsService.Log("untag", imgID.String(), "")
  161. *records = append(*records, untaggedRecord)
  162. }
  163. return nil
  164. }
  165. // ImageDeleteConflict holds a soft or hard conflict and an associated error.
  166. // Implements the error interface.
  167. type imageDeleteConflict struct {
  168. hard bool
  169. used bool
  170. imgID image.ID
  171. message string
  172. }
  173. func (idc *imageDeleteConflict) Error() string {
  174. var forceMsg string
  175. if idc.hard {
  176. forceMsg = "cannot be forced"
  177. } else {
  178. forceMsg = "must be forced"
  179. }
  180. return fmt.Sprintf("conflict: unable to delete %s (%s) - %s", stringid.TruncateID(idc.imgID.String()), forceMsg, idc.message)
  181. }
  182. // imageDeleteHelper attempts to delete the given image from this daemon. If
  183. // the image has any hard delete conflicts (child images or running containers
  184. // using the image) then it cannot be deleted. If the image has any soft delete
  185. // conflicts (any tags/digests referencing the image or any stopped container
  186. // using the image) then it can only be deleted if force is true. If the delete
  187. // succeeds and prune is true, the parent images are also deleted if they do
  188. // not have any soft or hard delete conflicts themselves. Any deleted images
  189. // and untagged references are appended to the given records. If any error or
  190. // conflict is encountered, it will be returned immediately without deleting
  191. // the image. If quiet is true, any encountered conflicts will be ignored and
  192. // the function will return nil immediately without deleting the image.
  193. func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDelete, force, prune, quiet bool) error {
  194. // First, determine if this image has any conflicts. Ignore soft conflicts
  195. // if force is true.
  196. if conflict := daemon.checkImageDeleteConflict(imgID, force); conflict != nil {
  197. if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) {
  198. // Ignore conflicts UNLESS the image is "dangling" or not being used in
  199. // which case we want the user to know.
  200. return nil
  201. }
  202. // There was a conflict and it's either a hard conflict OR we are not
  203. // forcing deletion on soft conflicts.
  204. return conflict
  205. }
  206. parent, err := daemon.imageStore.GetParent(imgID)
  207. if err != nil {
  208. // There may be no parent
  209. parent = ""
  210. }
  211. // Delete all repository tag/digest references to this image.
  212. if err := daemon.removeAllReferencesToImageID(imgID, records); err != nil {
  213. return err
  214. }
  215. removedLayers, err := daemon.imageStore.Delete(imgID)
  216. if err != nil {
  217. return err
  218. }
  219. daemon.EventsService.Log("delete", imgID.String(), "")
  220. *records = append(*records, types.ImageDelete{Deleted: imgID.String()})
  221. for _, removedLayer := range removedLayers {
  222. *records = append(*records, types.ImageDelete{Deleted: removedLayer.ChainID.String()})
  223. }
  224. if !prune || parent == "" {
  225. return nil
  226. }
  227. // We need to prune the parent image. This means delete it if there are
  228. // no tags/digests referencing it and there are no containers using it (
  229. // either running or stopped).
  230. // Do not force prunings, but do so quietly (stopping on any encountered
  231. // conflicts).
  232. return daemon.imageDeleteHelper(parent, records, false, true, true)
  233. }
  234. // checkImageDeleteConflict determines whether there are any conflicts
  235. // preventing deletion of the given image from this daemon. A hard conflict is
  236. // any image which has the given image as a parent or any running container
  237. // using the image. A soft conflict is any tags/digest referencing the given
  238. // image or any stopped container using the image. If ignoreSoftConflicts is
  239. // true, this function will not check for soft conflict conditions.
  240. func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, ignoreSoftConflicts bool) *imageDeleteConflict {
  241. // Check for hard conflicts first.
  242. if conflict := daemon.checkImageDeleteHardConflict(imgID); conflict != nil {
  243. return conflict
  244. }
  245. // Then check for soft conflicts.
  246. if ignoreSoftConflicts {
  247. // Don't bother checking for soft conflicts.
  248. return nil
  249. }
  250. return daemon.checkImageDeleteSoftConflict(imgID)
  251. }
  252. func (daemon *Daemon) checkImageDeleteHardConflict(imgID image.ID) *imageDeleteConflict {
  253. // Check if the image has any descendent images.
  254. if len(daemon.imageStore.Children(imgID)) > 0 {
  255. return &imageDeleteConflict{
  256. hard: true,
  257. imgID: imgID,
  258. message: "image has dependent child images",
  259. }
  260. }
  261. // Check if any running container is using the image.
  262. for _, container := range daemon.List() {
  263. if !container.IsRunning() {
  264. // Skip this until we check for soft conflicts later.
  265. continue
  266. }
  267. if container.ImageID == imgID {
  268. return &imageDeleteConflict{
  269. imgID: imgID,
  270. hard: true,
  271. used: true,
  272. message: fmt.Sprintf("image is being used by running container %s", stringid.TruncateID(container.ID)),
  273. }
  274. }
  275. }
  276. return nil
  277. }
  278. func (daemon *Daemon) checkImageDeleteSoftConflict(imgID image.ID) *imageDeleteConflict {
  279. // Check if any repository tags/digest reference this image.
  280. if len(daemon.tagStore.References(imgID)) > 0 {
  281. return &imageDeleteConflict{
  282. imgID: imgID,
  283. message: "image is referenced in one or more repositories",
  284. }
  285. }
  286. // Check if any stopped containers reference this image.
  287. for _, container := range daemon.List() {
  288. if container.IsRunning() {
  289. // Skip this as it was checked above in hard conflict conditions.
  290. continue
  291. }
  292. if container.ImageID == imgID {
  293. return &imageDeleteConflict{
  294. imgID: imgID,
  295. used: true,
  296. message: fmt.Sprintf("image is being used by stopped container %s", stringid.TruncateID(container.ID)),
  297. }
  298. }
  299. }
  300. return nil
  301. }
  302. // imageIsDangling returns whether the given image is "dangling" which means
  303. // that there are no repository references to the given image and it has no
  304. // child images.
  305. func (daemon *Daemon) imageIsDangling(imgID image.ID) bool {
  306. return !(len(daemon.tagStore.References(imgID)) > 0 || len(daemon.imageStore.Children(imgID)) > 0)
  307. }