soft_delete.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package containerd
  2. import (
  3. "context"
  4. cerrdefs "github.com/containerd/containerd/errdefs"
  5. containerdimages "github.com/containerd/containerd/images"
  6. "github.com/docker/docker/errdefs"
  7. "github.com/docker/docker/internal/compatcontext"
  8. "github.com/opencontainers/go-digest"
  9. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  10. "github.com/pkg/errors"
  11. )
  12. const imageNameDanglingPrefix = "moby-dangling@"
  13. // softImageDelete deletes the image, making sure that there are other images
  14. // that reference the content of the deleted image.
  15. // If no other image exists, a dangling one is created.
  16. func (i *ImageService) softImageDelete(ctx context.Context, img containerdimages.Image) error {
  17. is := i.client.ImageService()
  18. // If the image already exists, persist it as dangling image
  19. // but only if no other image has the same target.
  20. dgst := img.Target.Digest.String()
  21. imgs, err := is.List(ctx, "target.digest=="+dgst)
  22. if err != nil {
  23. return errdefs.System(errors.Wrapf(err, "failed to check if there are images targeting digest %s", dgst))
  24. }
  25. // From this point explicitly ignore the passed context
  26. // and don't allow to interrupt operation in the middle.
  27. // Create dangling image if this is the last image pointing to this target.
  28. if len(imgs) == 1 {
  29. err = i.ensureDanglingImage(compatcontext.WithoutCancel(ctx), img)
  30. // Error out in case we couldn't persist the old image.
  31. if err != nil {
  32. return errdefs.System(errors.Wrapf(err, "failed to create a dangling image for the replaced image %s with digest %s",
  33. img.Name, img.Target.Digest.String()))
  34. }
  35. }
  36. // Free the target name.
  37. err = is.Delete(compatcontext.WithoutCancel(ctx), img.Name)
  38. if err != nil {
  39. if !cerrdefs.IsNotFound(err) {
  40. return errdefs.System(errors.Wrapf(err, "failed to delete image %s which existed a moment before", img.Name))
  41. }
  42. }
  43. return nil
  44. }
  45. func (i *ImageService) ensureDanglingImage(ctx context.Context, from containerdimages.Image) error {
  46. danglingImage := from
  47. danglingImage.Labels = make(map[string]string)
  48. for k, v := range from.Labels {
  49. switch k {
  50. case containerdimages.AnnotationImageName, ocispec.AnnotationRefName:
  51. // Don't copy name labels.
  52. default:
  53. danglingImage.Labels[k] = v
  54. }
  55. }
  56. danglingImage.Name = danglingImageName(from.Target.Digest)
  57. _, err := i.client.ImageService().Create(compatcontext.WithoutCancel(ctx), danglingImage)
  58. // If it already exists, then just continue.
  59. if cerrdefs.IsAlreadyExists(err) {
  60. return nil
  61. }
  62. return err
  63. }
  64. func danglingImageName(digest digest.Digest) string {
  65. return imageNameDanglingPrefix + digest.String()
  66. }
  67. func isDanglingImage(image containerdimages.Image) bool {
  68. return image.Name == danglingImageName(image.Target.Digest)
  69. }