github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/containerd/soft_delete.go (about)

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