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

     1  package containerd
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/Prakhar-Agarwal-byte/moby/errdefs"
     7  	"github.com/Prakhar-Agarwal-byte/moby/image"
     8  	"github.com/containerd/containerd/content"
     9  	cerrdefs "github.com/containerd/containerd/errdefs"
    10  	containerdimages "github.com/containerd/containerd/images"
    11  	"github.com/containerd/containerd/platforms"
    12  	"github.com/containerd/log"
    13  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // Children returns a slice of image IDs that are children of the `id` image
    18  func (i *ImageService) Children(ctx context.Context, id image.ID) ([]image.ID, error) {
    19  	imgs, err := i.client.ImageService().List(ctx, "labels."+imageLabelClassicBuilderParent+"=="+string(id))
    20  	if err != nil {
    21  		return []image.ID{}, errdefs.System(errors.Wrap(err, "failed to list all images"))
    22  	}
    23  
    24  	var children []image.ID
    25  	for _, img := range imgs {
    26  		children = append(children, image.ID(img.Target.Digest))
    27  	}
    28  
    29  	return children, nil
    30  }
    31  
    32  // platformRootfs returns a rootfs for a specified platform.
    33  func platformRootfs(ctx context.Context, store content.Store, desc ocispec.Descriptor, platform ocispec.Platform) (ocispec.RootFS, error) {
    34  	empty := ocispec.RootFS{}
    35  
    36  	configDesc, err := containerdimages.Config(ctx, store, desc, platforms.OnlyStrict(platform))
    37  	if err != nil {
    38  		return empty, errors.Wrapf(err, "failed to get config for platform %s", platforms.Format(platform))
    39  	}
    40  
    41  	diffs, err := containerdimages.RootFS(ctx, store, configDesc)
    42  	if err != nil {
    43  		return empty, errors.Wrapf(err, "failed to obtain rootfs")
    44  	}
    45  
    46  	return ocispec.RootFS{
    47  		Type:    "layers",
    48  		DiffIDs: diffs,
    49  	}, nil
    50  }
    51  
    52  // isRootfsChildOf checks if all layers from parent rootfs are child's first layers
    53  // and child has at least one more layer (to make it not commutative).
    54  // Example:
    55  // A with layers [X, Y],
    56  // B with layers [X, Y, Z]
    57  // C with layers [Y, Z]
    58  //
    59  // Only isRootfsChildOf(B, A) is true.
    60  // Which means that B is considered a children of A. B and C has no children.
    61  // See more examples in TestIsRootfsChildOf.
    62  func isRootfsChildOf(child ocispec.RootFS, parent ocispec.RootFS) bool {
    63  	childLen := len(child.DiffIDs)
    64  	parentLen := len(parent.DiffIDs)
    65  
    66  	if childLen <= parentLen {
    67  		return false
    68  	}
    69  
    70  	for i := 0; i < parentLen; i++ {
    71  		if child.DiffIDs[i] != parent.DiffIDs[i] {
    72  			return false
    73  		}
    74  	}
    75  
    76  	return true
    77  }
    78  
    79  // parents returns a slice of image IDs whose entire rootfs contents match,
    80  // in order, the childs first layers, excluding images with the exact same
    81  // rootfs.
    82  //
    83  // Called from image_delete.go to prune dangling parents.
    84  func (i *ImageService) parents(ctx context.Context, id image.ID) ([]imageWithRootfs, error) {
    85  	target, err := i.resolveDescriptor(ctx, id.String())
    86  	if err != nil {
    87  		return nil, errors.Wrap(err, "failed to get child image")
    88  	}
    89  
    90  	cs := i.client.ContentStore()
    91  
    92  	allPlatforms, err := containerdimages.Platforms(ctx, cs, target)
    93  	if err != nil {
    94  		return nil, errdefs.System(errors.Wrap(err, "failed to list platforms supported by image"))
    95  	}
    96  
    97  	var childRootFS []ocispec.RootFS
    98  	for _, platform := range allPlatforms {
    99  		rootfs, err := platformRootfs(ctx, cs, target, platform)
   100  		if err != nil {
   101  			if cerrdefs.IsNotFound(err) {
   102  				continue
   103  			}
   104  			return nil, errdefs.System(errors.Wrap(err, "failed to get platform-specific rootfs"))
   105  		}
   106  
   107  		childRootFS = append(childRootFS, rootfs)
   108  	}
   109  
   110  	imgs, err := i.client.ImageService().List(ctx)
   111  	if err != nil {
   112  		return nil, errdefs.System(errors.Wrap(err, "failed to list all images"))
   113  	}
   114  
   115  	var parents []imageWithRootfs
   116  	for _, img := range imgs {
   117  	nextImage:
   118  		for _, platform := range allPlatforms {
   119  			rootfs, err := platformRootfs(ctx, cs, img.Target, platform)
   120  			if err != nil {
   121  				if cerrdefs.IsNotFound(err) {
   122  					continue
   123  				}
   124  				return nil, errdefs.System(errors.Wrap(err, "failed to get platform-specific rootfs"))
   125  			}
   126  
   127  			for _, childRoot := range childRootFS {
   128  				if isRootfsChildOf(childRoot, rootfs) {
   129  					parents = append(parents, imageWithRootfs{
   130  						img:    img,
   131  						rootfs: rootfs,
   132  					})
   133  					break nextImage
   134  				}
   135  			}
   136  		}
   137  	}
   138  
   139  	return parents, nil
   140  }
   141  
   142  // getParentsByBuilderLabel finds images that were a base for the given image
   143  // by an image label set by the legacy builder.
   144  // NOTE: This only works for images built with legacy builder (not Buildkit).
   145  func (i *ImageService) getParentsByBuilderLabel(ctx context.Context, img containerdimages.Image) ([]containerdimages.Image, error) {
   146  	parent, ok := img.Labels[imageLabelClassicBuilderParent]
   147  	if !ok || parent == "" {
   148  		return nil, nil
   149  	}
   150  
   151  	dgst, err := digest.Parse(parent)
   152  	if err != nil {
   153  		log.G(ctx).WithFields(log.Fields{
   154  			"error": err,
   155  			"value": parent,
   156  		}).Warnf("invalid %s label value", imageLabelClassicBuilderParent)
   157  		return nil, nil
   158  	}
   159  
   160  	return i.client.ImageService().List(ctx, "target.digest=="+dgst.String())
   161  }
   162  
   163  type imageWithRootfs struct {
   164  	img    containerdimages.Image
   165  	rootfs ocispec.RootFS
   166  }