package containerd import ( "context" cerrdefs "github.com/containerd/containerd/errdefs" containerdimages "github.com/containerd/containerd/images" "github.com/docker/docker/errdefs" "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) // 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) error { is := i.client.ImageService() // If the image already exists, persist it as dangling image // but only if no other image has the same target. digest := img.Target.Digest.String() imgs, err := is.List(ctx, "target.digest=="+digest) if err != nil { return errdefs.System(errors.Wrapf(err, "failed to check if there are images targeting digest %s", digest)) } // 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 { danglingImage := img danglingImage.Name = danglingImageName(img.Target.Digest) delete(danglingImage.Labels, "io.containerd.image.name") delete(danglingImage.Labels, "org.opencontainers.image.ref.name") _, err = is.Create(context.Background(), danglingImage) // Error out in case we couldn't persist the old image. // If it already exists, then just continue. if err != nil && !cerrdefs.IsAlreadyExists(err) { return errdefs.System(errors.Wrapf(err, "failed to create a dangling image for the replaced image %s with digest %s", danglingImage.Name, danglingImage.Target.Digest.String())) } } // Free the target name. err = is.Delete(context.Background(), 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 danglingImageName(digest digest.Digest) string { return "moby-dangling@" + digest.String() } func isDanglingImage(image containerdimages.Image) bool { return image.Name == danglingImageName(image.Target.Digest) }