| package containerd |
| |
| import ( |
| "context" |
| |
| cerrdefs "github.com/containerd/containerd/errdefs" |
| containerdimages "github.com/containerd/containerd/images" |
| "github.com/docker/docker/errdefs" |
| "github.com/docker/docker/internal/compatcontext" |
| "github.com/opencontainers/go-digest" |
| ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
| "github.com/pkg/errors" |
| ) |
| |
| const imageNameDanglingPrefix = "moby-dangling@" |
| |
| // softImageDelete deletes the image, making sure that there are other images |
| // that reference the content of the deleted image. |
| // If no other image exists, a dangling one is created. |
| func (i *ImageService) softImageDelete(ctx context.Context, img containerdimages.Image, imgs []containerdimages.Image) error { |
| // From this point explicitly ignore the passed context |
| // and don't allow to interrupt operation in the middle. |
| |
| // Create dangling image if this is the last image pointing to this target. |
| if len(imgs) == 1 { |
| err := i.ensureDanglingImage(compatcontext.WithoutCancel(ctx), img) |
| |
| // Error out in case we couldn't persist the old image. |
| if err != nil { |
| return errdefs.System(errors.Wrapf(err, "failed to create a dangling image for the replaced image %s with digest %s", |
| img.Name, img.Target.Digest.String())) |
| } |
| } |
| |
| // Free the target name. |
| // TODO: Add with target option |
| err := i.images.Delete(compatcontext.WithoutCancel(ctx), img.Name) |
| if err != nil { |
| if !cerrdefs.IsNotFound(err) { |
| return errdefs.System(errors.Wrapf(err, "failed to delete image %s which existed a moment before", img.Name)) |
| } |
| } |
| |
| return nil |
| } |
| |
| func (i *ImageService) ensureDanglingImage(ctx context.Context, from containerdimages.Image) error { |
| danglingImage := from |
| |
| danglingImage.Labels = make(map[string]string) |
| for k, v := range from.Labels { |
| switch k { |
| case containerdimages.AnnotationImageName, ocispec.AnnotationRefName: |
| // Don't copy name labels. |
| default: |
| danglingImage.Labels[k] = v |
| } |
| } |
| danglingImage.Name = danglingImageName(from.Target.Digest) |
| |
| _, err := i.images.Create(compatcontext.WithoutCancel(ctx), danglingImage) |
| // If it already exists, then just continue. |
| if cerrdefs.IsAlreadyExists(err) { |
| return nil |
| } |
| |
| return err |
| } |
| |
| func danglingImageName(digest digest.Digest) string { |
| return imageNameDanglingPrefix + digest.String() |
| } |
| |
| func isDanglingImage(image containerdimages.Image) bool { |
| // TODO: Also check for expired |
| return image.Name == danglingImageName(image.Target.Digest) |
| } |