| package containerd |
| |
| import ( |
| "context" |
| "fmt" |
| |
| "github.com/containerd/containerd/content" |
| "github.com/containerd/containerd/errdefs" |
| "github.com/containerd/containerd/images" |
| "github.com/containerd/containerd/platforms" |
| "github.com/containerd/containerd/rootfs" |
| "github.com/containerd/containerd/snapshots" |
| digest "github.com/opencontainers/go-digest" |
| "github.com/opencontainers/image-spec/identity" |
| ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
| "github.com/pkg/errors" |
| ) |
| |
| // Image describes an image used by containers |
| type Image interface { |
| // Name of the image |
| Name() string |
| // Target descriptor for the image content |
| Target() ocispec.Descriptor |
| // Unpack unpacks the image's content into a snapshot |
| Unpack(context.Context, string) error |
| // RootFS returns the unpacked diffids that make up images rootfs. |
| RootFS(ctx context.Context) ([]digest.Digest, error) |
| // Size returns the total size of the image's packed resources. |
| Size(ctx context.Context) (int64, error) |
| // Config descriptor for the image. |
| Config(ctx context.Context) (ocispec.Descriptor, error) |
| // IsUnpacked returns whether or not an image is unpacked. |
| IsUnpacked(context.Context, string) (bool, error) |
| // ContentStore provides a content store which contains image blob data |
| ContentStore() content.Store |
| } |
| |
| var _ = (Image)(&image{}) |
| |
| type image struct { |
| client *Client |
| |
| i images.Image |
| } |
| |
| func (i *image) Name() string { |
| return i.i.Name |
| } |
| |
| func (i *image) Target() ocispec.Descriptor { |
| return i.i.Target |
| } |
| |
| func (i *image) RootFS(ctx context.Context) ([]digest.Digest, error) { |
| provider := i.client.ContentStore() |
| return i.i.RootFS(ctx, provider, platforms.Default()) |
| } |
| |
| func (i *image) Size(ctx context.Context) (int64, error) { |
| provider := i.client.ContentStore() |
| return i.i.Size(ctx, provider, platforms.Default()) |
| } |
| |
| func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) { |
| provider := i.client.ContentStore() |
| return i.i.Config(ctx, provider, platforms.Default()) |
| } |
| |
| func (i *image) IsUnpacked(ctx context.Context, snapshotterName string) (bool, error) { |
| sn := i.client.SnapshotService(snapshotterName) |
| cs := i.client.ContentStore() |
| |
| diffs, err := i.i.RootFS(ctx, cs, platforms.Default()) |
| if err != nil { |
| return false, err |
| } |
| |
| chainID := identity.ChainID(diffs) |
| _, err = sn.Stat(ctx, chainID.String()) |
| if err == nil { |
| return true, nil |
| } else if !errdefs.IsNotFound(err) { |
| return false, err |
| } |
| |
| return false, nil |
| } |
| |
| func (i *image) Unpack(ctx context.Context, snapshotterName string) error { |
| ctx, done, err := i.client.WithLease(ctx) |
| if err != nil { |
| return err |
| } |
| defer done() |
| |
| layers, err := i.getLayers(ctx, platforms.Default()) |
| if err != nil { |
| return err |
| } |
| |
| var ( |
| sn = i.client.SnapshotService(snapshotterName) |
| a = i.client.DiffService() |
| cs = i.client.ContentStore() |
| |
| chain []digest.Digest |
| unpacked bool |
| ) |
| for _, layer := range layers { |
| labels := map[string]string{ |
| "containerd.io/uncompressed": layer.Diff.Digest.String(), |
| } |
| |
| unpacked, err = rootfs.ApplyLayer(ctx, layer, chain, sn, a, snapshots.WithLabels(labels)) |
| if err != nil { |
| return err |
| } |
| |
| chain = append(chain, layer.Diff.Digest) |
| } |
| |
| if unpacked { |
| desc, err := i.i.Config(ctx, cs, platforms.Default()) |
| if err != nil { |
| return err |
| } |
| |
| rootfs := identity.ChainID(chain).String() |
| |
| cinfo := content.Info{ |
| Digest: desc.Digest, |
| Labels: map[string]string{ |
| fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", snapshotterName): rootfs, |
| }, |
| } |
| if _, err := cs.Update(ctx, cinfo, fmt.Sprintf("labels.containerd.io/gc.ref.snapshot.%s", snapshotterName)); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func (i *image) getLayers(ctx context.Context, platform string) ([]rootfs.Layer, error) { |
| cs := i.client.ContentStore() |
| |
| manifest, err := images.Manifest(ctx, cs, i.i.Target, platform) |
| if err != nil { |
| return nil, errors.Wrap(err, "") |
| } |
| |
| diffIDs, err := i.i.RootFS(ctx, cs, platform) |
| if err != nil { |
| return nil, errors.Wrap(err, "failed to resolve rootfs") |
| } |
| if len(diffIDs) != len(manifest.Layers) { |
| return nil, errors.Errorf("mismatched image rootfs and manifest layers") |
| } |
| layers := make([]rootfs.Layer, len(diffIDs)) |
| for i := range diffIDs { |
| layers[i].Diff = ocispec.Descriptor{ |
| // TODO: derive media type from compressed type |
| MediaType: ocispec.MediaTypeImageLayer, |
| Digest: diffIDs[i], |
| } |
| layers[i].Blob = manifest.Layers[i] |
| } |
| return layers, nil |
| } |
| |
| func (i *image) ContentStore() content.Store { |
| return i.client.ContentStore() |
| } |