94b4765363
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>
151 lines
4.2 KiB
Go
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
|
|
}
|