123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- 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"
- cplatforms "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, cplatforms.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 := cplatforms.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
- }
|