github.com/moby/docker@v26.1.3+incompatible/daemon/containerd/image_snapshot.go (about)

     1  package containerd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/containerd/containerd"
     8  	cerrdefs "github.com/containerd/containerd/errdefs"
     9  	containerdimages "github.com/containerd/containerd/images"
    10  	"github.com/containerd/containerd/leases"
    11  	"github.com/containerd/containerd/mount"
    12  	"github.com/containerd/containerd/platforms"
    13  	"github.com/containerd/containerd/snapshots"
    14  	"github.com/docker/docker/errdefs"
    15  	"github.com/opencontainers/image-spec/identity"
    16  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  // PrepareSnapshot prepares a snapshot from a parent image for a container
    21  func (i *ImageService) PrepareSnapshot(ctx context.Context, id string, parentImage string, platform *ocispec.Platform, setupInit func(string) error) error {
    22  	var parentSnapshot string
    23  	if parentImage != "" {
    24  		img, err := i.resolveImage(ctx, parentImage)
    25  		if err != nil {
    26  			return err
    27  		}
    28  
    29  		cs := i.content
    30  
    31  		matcher := matchAllWithPreference(platforms.Default())
    32  		if platform != nil {
    33  			matcher = platforms.Only(*platform)
    34  		}
    35  
    36  		platformImg := containerd.NewImageWithPlatform(i.client, img, matcher)
    37  		unpacked, err := platformImg.IsUnpacked(ctx, i.snapshotter)
    38  		if err != nil {
    39  			return err
    40  		}
    41  
    42  		if !unpacked {
    43  			if err := platformImg.Unpack(ctx, i.snapshotter); err != nil {
    44  				return err
    45  			}
    46  		}
    47  
    48  		desc, err := containerdimages.Config(ctx, cs, img.Target, matcher)
    49  		if err != nil {
    50  			return err
    51  		}
    52  
    53  		diffIDs, err := containerdimages.RootFS(ctx, cs, desc)
    54  		if err != nil {
    55  			return err
    56  		}
    57  
    58  		parentSnapshot = identity.ChainID(diffIDs).String()
    59  	}
    60  
    61  	ls := i.client.LeasesService()
    62  	lease, err := ls.Create(ctx, leases.WithID(id))
    63  	if err != nil {
    64  		return err
    65  	}
    66  	ctx = leases.WithLease(ctx, lease.ID)
    67  
    68  	snapshotter := i.client.SnapshotService(i.StorageDriver())
    69  
    70  	if err := i.prepareInitLayer(ctx, id, parentSnapshot, setupInit); err != nil {
    71  		return err
    72  	}
    73  
    74  	if !i.idMapping.Empty() {
    75  		return i.remapSnapshot(ctx, snapshotter, id, id+"-init")
    76  	}
    77  
    78  	_, err = snapshotter.Prepare(ctx, id, id+"-init")
    79  	return err
    80  }
    81  
    82  func (i *ImageService) prepareInitLayer(ctx context.Context, id string, parent string, setupInit func(string) error) error {
    83  	snapshotter := i.client.SnapshotService(i.StorageDriver())
    84  
    85  	mounts, err := snapshotter.Prepare(ctx, id+"-init-key", parent)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	if setupInit != nil {
    91  		if err := mount.WithTempMount(ctx, mounts, func(root string) error {
    92  			return setupInit(root)
    93  		}); err != nil {
    94  			return err
    95  		}
    96  	}
    97  
    98  	return snapshotter.Commit(ctx, id+"-init", id+"-init-key")
    99  }
   100  
   101  // calculateSnapshotParentUsage returns the usage of all ancestors of the
   102  // provided snapshot. It doesn't include the size of the snapshot itself.
   103  func calculateSnapshotParentUsage(ctx context.Context, snapshotter snapshots.Snapshotter, snapshotID string) (snapshots.Usage, error) {
   104  	info, err := snapshotter.Stat(ctx, snapshotID)
   105  	if err != nil {
   106  		if cerrdefs.IsNotFound(err) {
   107  			return snapshots.Usage{}, errdefs.NotFound(err)
   108  		}
   109  		return snapshots.Usage{}, errdefs.System(errors.Wrapf(err, "snapshotter.Stat failed for %s", snapshotID))
   110  	}
   111  	if info.Parent == "" {
   112  		return snapshots.Usage{}, errdefs.NotFound(fmt.Errorf("snapshot %s has no parent", snapshotID))
   113  	}
   114  
   115  	return calculateSnapshotTotalUsage(ctx, snapshotter, info.Parent)
   116  }
   117  
   118  // calculateSnapshotTotalUsage returns the total usage of that snapshot
   119  // including all of its ancestors.
   120  func calculateSnapshotTotalUsage(ctx context.Context, snapshotter snapshots.Snapshotter, snapshotID string) (snapshots.Usage, error) {
   121  	var total snapshots.Usage
   122  	next := snapshotID
   123  
   124  	for next != "" {
   125  		usage, err := snapshotter.Usage(ctx, next)
   126  		if err != nil {
   127  			if cerrdefs.IsNotFound(err) {
   128  				return total, errdefs.NotFound(errors.Wrapf(err, "non-existing ancestor of %s", snapshotID))
   129  			}
   130  			return total, errdefs.System(errors.Wrapf(err, "snapshotter.Usage failed for %s", next))
   131  		}
   132  		total.Size += usage.Size
   133  		total.Inodes += usage.Inodes
   134  
   135  		info, err := snapshotter.Stat(ctx, next)
   136  		if err != nil {
   137  			return total, errdefs.System(errors.Wrapf(err, "snapshotter.Stat failed for %s", next))
   138  		}
   139  		next = info.Parent
   140  	}
   141  	return total, nil
   142  }