moby/daemon/containerd/image_manifest.go
Sebastiaan van Stijn 94b4765363
pkg/platforms: internalize in daemon/containerd
This matcher was only used internally in the containerd implementation of
the image store. Un-export it, and make it a local utility in that package
to prevent external use.

This package was introduced in 1616a09b61
(v24.0), and there are no known external consumers of this package, so there
should be no need to deprecate / alias the old location.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-01-20 22:28:56 +01:00

151 lines
4.2 KiB
Go

package containerd
import (
"context"
"encoding/json"
"github.com/containerd/containerd"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
containerdimages "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/errdefs"
"github.com/moby/buildkit/util/attestation"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
var (
errNotManifestOrIndex = errdefs.InvalidParameter(errors.New("descriptor is neither a manifest or index"))
errNotManifest = errdefs.InvalidParameter(errors.New("descriptor isn't a manifest"))
)
// walkImageManifests calls the handler for each locally present manifest in
// the image. The image implements the containerd.Image interface, but all
// operations act on the specific manifest instead of the index.
func (i *ImageService) walkImageManifests(ctx context.Context, img containerdimages.Image, handler func(img *ImageManifest) error) error {
desc := img.Target
handleManifest := func(ctx context.Context, d ocispec.Descriptor) error {
platformImg, err := i.NewImageManifest(ctx, img, d)
if err != nil {
if err == errNotManifest {
return nil
}
return err
}
return handler(platformImg)
}
if containerdimages.IsManifestType(desc.MediaType) {
return handleManifest(ctx, desc)
}
if containerdimages.IsIndexType(desc.MediaType) {
return i.walkPresentChildren(ctx, desc, handleManifest)
}
return errNotManifestOrIndex
}
type ImageManifest struct {
containerd.Image
// Parent of the manifest (index/manifest list)
RealTarget ocispec.Descriptor
manifest *ocispec.Manifest
}
func (i *ImageService) NewImageManifest(ctx context.Context, img containerdimages.Image, manifestDesc ocispec.Descriptor) (*ImageManifest, error) {
if !containerdimages.IsManifestType(manifestDesc.MediaType) {
return nil, errNotManifest
}
parent := img.Target
img.Target = manifestDesc
c8dImg := containerd.NewImageWithPlatform(i.client, img, platforms.All)
return &ImageManifest{
Image: c8dImg,
RealTarget: parent,
}, nil
}
func (im *ImageManifest) Metadata() containerdimages.Image {
md := im.Image.Metadata()
md.Target = im.RealTarget
return md
}
// IsPseudoImage returns false if the manifest has no layers or any of its layers is a known image layer.
// Some manifests use the image media type for compatibility, even if they are not a real image.
func (im *ImageManifest) IsPseudoImage(ctx context.Context) (bool, error) {
desc := im.Target()
// Quick check for buildkit attestation manifests
// https://github.com/moby/buildkit/blob/v0.11.4/docs/attestations/attestation-storage.md
// This would have also been caught by the layer check below, but it requires
// an additional content read and deserialization of Manifest.
if _, has := desc.Annotations[attestation.DockerAnnotationReferenceType]; has {
return true, nil
}
mfst, err := im.Manifest(ctx)
if err != nil {
return true, err
}
if len(mfst.Layers) == 0 {
return false, nil
}
for _, l := range mfst.Layers {
if images.IsLayerType(l.MediaType) {
return false, nil
}
}
return true, nil
}
func (im *ImageManifest) Manifest(ctx context.Context) (ocispec.Manifest, error) {
if im.manifest != nil {
return *im.manifest, nil
}
mfst, err := readManifest(ctx, im.ContentStore(), im.Target())
if err != nil {
return ocispec.Manifest{}, err
}
im.manifest = &mfst
return mfst, nil
}
func (im *ImageManifest) CheckContentAvailable(ctx context.Context) (bool, error) {
// The target is already a platform-specific manifest, so no need to match platform.
pm := platforms.All
available, _, _, missing, err := containerdimages.Check(ctx, im.ContentStore(), im.Target(), pm)
if err != nil {
return false, err
}
if !available || len(missing) > 0 {
return false, nil
}
return true, nil
}
func readManifest(ctx context.Context, store content.Provider, desc ocispec.Descriptor) (ocispec.Manifest, error) {
p, err := content.ReadBlob(ctx, store, desc)
if err != nil {
return ocispec.Manifest{}, err
}
var mfst ocispec.Manifest
if err := json.Unmarshal(p, &mfst); err != nil {
return ocispec.Manifest{}, err
}
return mfst, nil
}