github.com/moby/docker@v26.1.3+incompatible/daemon/containerd/image_manifest.go (about) 1 package containerd 2 3 import ( 4 "context" 5 "encoding/json" 6 7 "github.com/containerd/containerd" 8 "github.com/containerd/containerd/content" 9 "github.com/containerd/containerd/images" 10 containerdimages "github.com/containerd/containerd/images" 11 "github.com/containerd/containerd/platforms" 12 "github.com/docker/docker/errdefs" 13 "github.com/moby/buildkit/util/attestation" 14 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 15 "github.com/pkg/errors" 16 ) 17 18 var ( 19 errNotManifestOrIndex = errdefs.InvalidParameter(errors.New("descriptor is neither a manifest or index")) 20 errNotManifest = errdefs.InvalidParameter(errors.New("descriptor isn't a manifest")) 21 ) 22 23 // walkImageManifests calls the handler for each locally present manifest in 24 // the image. The image implements the containerd.Image interface, but all 25 // operations act on the specific manifest instead of the index. 26 func (i *ImageService) walkImageManifests(ctx context.Context, img containerdimages.Image, handler func(img *ImageManifest) error) error { 27 desc := img.Target 28 29 handleManifest := func(ctx context.Context, d ocispec.Descriptor) error { 30 platformImg, err := i.NewImageManifest(ctx, img, d) 31 if err != nil { 32 if err == errNotManifest { 33 return nil 34 } 35 return err 36 } 37 return handler(platformImg) 38 } 39 40 if containerdimages.IsManifestType(desc.MediaType) { 41 return handleManifest(ctx, desc) 42 } 43 44 if containerdimages.IsIndexType(desc.MediaType) { 45 return i.walkPresentChildren(ctx, desc, handleManifest) 46 } 47 48 return errors.Wrapf(errNotManifestOrIndex, "error walking manifest for %s", img.Name) 49 } 50 51 type ImageManifest struct { 52 containerd.Image 53 54 // Parent of the manifest (index/manifest list) 55 RealTarget ocispec.Descriptor 56 57 manifest *ocispec.Manifest 58 } 59 60 func (i *ImageService) NewImageManifest(ctx context.Context, img containerdimages.Image, manifestDesc ocispec.Descriptor) (*ImageManifest, error) { 61 if !containerdimages.IsManifestType(manifestDesc.MediaType) { 62 return nil, errNotManifest 63 } 64 65 parent := img.Target 66 img.Target = manifestDesc 67 68 c8dImg := containerd.NewImageWithPlatform(i.client, img, platforms.All) 69 return &ImageManifest{ 70 Image: c8dImg, 71 RealTarget: parent, 72 }, nil 73 } 74 75 func (im *ImageManifest) Metadata() containerdimages.Image { 76 md := im.Image.Metadata() 77 md.Target = im.RealTarget 78 return md 79 } 80 81 // IsPseudoImage returns false if the manifest has no layers or any of its layers is a known image layer. 82 // Some manifests use the image media type for compatibility, even if they are not a real image. 83 func (im *ImageManifest) IsPseudoImage(ctx context.Context) (bool, error) { 84 desc := im.Target() 85 86 // Quick check for buildkit attestation manifests 87 // https://github.com/moby/buildkit/blob/v0.11.4/docs/attestations/attestation-storage.md 88 // This would have also been caught by the layer check below, but it requires 89 // an additional content read and deserialization of Manifest. 90 if _, has := desc.Annotations[attestation.DockerAnnotationReferenceType]; has { 91 return true, nil 92 } 93 94 mfst, err := im.Manifest(ctx) 95 if err != nil { 96 return true, err 97 } 98 if len(mfst.Layers) == 0 { 99 return false, nil 100 } 101 for _, l := range mfst.Layers { 102 if images.IsLayerType(l.MediaType) { 103 return false, nil 104 } 105 } 106 return true, nil 107 } 108 109 func (im *ImageManifest) Manifest(ctx context.Context) (ocispec.Manifest, error) { 110 if im.manifest != nil { 111 return *im.manifest, nil 112 } 113 114 mfst, err := readManifest(ctx, im.ContentStore(), im.Target()) 115 if err != nil { 116 return ocispec.Manifest{}, err 117 } 118 119 im.manifest = &mfst 120 return mfst, nil 121 } 122 123 func (im *ImageManifest) CheckContentAvailable(ctx context.Context) (bool, error) { 124 // The target is already a platform-specific manifest, so no need to match platform. 125 pm := platforms.All 126 127 available, _, _, missing, err := containerdimages.Check(ctx, im.ContentStore(), im.Target(), pm) 128 if err != nil { 129 return false, err 130 } 131 132 if !available || len(missing) > 0 { 133 return false, nil 134 } 135 136 return true, nil 137 } 138 139 func readManifest(ctx context.Context, store content.Provider, desc ocispec.Descriptor) (ocispec.Manifest, error) { 140 p, err := content.ReadBlob(ctx, store, desc) 141 if err != nil { 142 return ocispec.Manifest{}, err 143 } 144 145 var mfst ocispec.Manifest 146 if err := json.Unmarshal(p, &mfst); err != nil { 147 return ocispec.Manifest{}, err 148 } 149 150 return mfst, nil 151 }