github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/cli/manifest/types/types.go (about) 1 package types 2 3 import ( 4 "encoding/json" 5 6 "github.com/docker/distribution" 7 "github.com/docker/distribution/manifest/manifestlist" 8 "github.com/docker/distribution/manifest/ocischema" 9 "github.com/docker/distribution/manifest/schema2" 10 "github.com/docker/distribution/reference" 11 "github.com/opencontainers/go-digest" 12 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 13 "github.com/pkg/errors" 14 ) 15 16 // ImageManifest contains info to output for a manifest object. 17 type ImageManifest struct { 18 Ref *SerializableNamed 19 Descriptor ocispec.Descriptor 20 Raw []byte `json:",omitempty"` 21 22 // SchemaV2Manifest is used for inspection 23 SchemaV2Manifest *schema2.DeserializedManifest `json:",omitempty"` 24 // OCIManifest is used for inspection 25 OCIManifest *ocischema.DeserializedManifest `json:",omitempty"` 26 } 27 28 // OCIPlatform creates an OCI platform from a manifest list platform spec 29 func OCIPlatform(ps *manifestlist.PlatformSpec) *ocispec.Platform { 30 if ps == nil { 31 return nil 32 } 33 return &ocispec.Platform{ 34 Architecture: ps.Architecture, 35 OS: ps.OS, 36 OSVersion: ps.OSVersion, 37 OSFeatures: ps.OSFeatures, 38 Variant: ps.Variant, 39 } 40 } 41 42 // PlatformSpecFromOCI creates a platform spec from OCI platform 43 func PlatformSpecFromOCI(p *ocispec.Platform) *manifestlist.PlatformSpec { 44 if p == nil { 45 return nil 46 } 47 return &manifestlist.PlatformSpec{ 48 Architecture: p.Architecture, 49 OS: p.OS, 50 OSVersion: p.OSVersion, 51 OSFeatures: p.OSFeatures, 52 Variant: p.Variant, 53 } 54 } 55 56 // Blobs returns the digests for all the blobs referenced by this manifest 57 func (i ImageManifest) Blobs() []digest.Digest { 58 digests := []digest.Digest{} 59 switch { 60 case i.SchemaV2Manifest != nil: 61 for _, descriptor := range i.SchemaV2Manifest.References() { 62 digests = append(digests, descriptor.Digest) 63 } 64 case i.OCIManifest != nil: 65 for _, descriptor := range i.OCIManifest.References() { 66 digests = append(digests, descriptor.Digest) 67 } 68 } 69 return digests 70 } 71 72 // Payload returns the media type and bytes for the manifest 73 func (i ImageManifest) Payload() (string, []byte, error) { 74 // TODO: If available, read content from a content store by digest 75 switch { 76 case i.SchemaV2Manifest != nil: 77 return i.SchemaV2Manifest.Payload() 78 case i.OCIManifest != nil: 79 return i.OCIManifest.Payload() 80 default: 81 return "", nil, errors.Errorf("%s has no payload", i.Ref) 82 } 83 } 84 85 // References implements the distribution.Manifest interface. It delegates to 86 // the underlying manifest. 87 func (i ImageManifest) References() []distribution.Descriptor { 88 switch { 89 case i.SchemaV2Manifest != nil: 90 return i.SchemaV2Manifest.References() 91 case i.OCIManifest != nil: 92 return i.OCIManifest.References() 93 default: 94 return nil 95 } 96 } 97 98 // NewImageManifest returns a new ImageManifest object. The values for Platform 99 // are initialized from those in the image 100 func NewImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *schema2.DeserializedManifest) ImageManifest { 101 raw, err := manifest.MarshalJSON() 102 if err != nil { 103 raw = nil 104 } 105 106 return ImageManifest{ 107 Ref: &SerializableNamed{Named: ref}, 108 Descriptor: desc, 109 Raw: raw, 110 SchemaV2Manifest: manifest, 111 } 112 } 113 114 // NewOCIImageManifest returns a new ImageManifest object. The values for 115 // Platform are initialized from those in the image 116 func NewOCIImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *ocischema.DeserializedManifest) ImageManifest { 117 raw, err := manifest.MarshalJSON() 118 if err != nil { 119 raw = nil 120 } 121 122 return ImageManifest{ 123 Ref: &SerializableNamed{Named: ref}, 124 Descriptor: desc, 125 Raw: raw, 126 OCIManifest: manifest, 127 } 128 } 129 130 // SerializableNamed is a reference.Named that can be serialized and deserialized 131 // from JSON 132 type SerializableNamed struct { 133 reference.Named 134 } 135 136 // UnmarshalJSON loads the Named reference from JSON bytes 137 func (s *SerializableNamed) UnmarshalJSON(b []byte) error { 138 var raw string 139 if err := json.Unmarshal(b, &raw); err != nil { 140 return errors.Wrapf(err, "invalid named reference bytes: %s", b) 141 } 142 var err error 143 s.Named, err = reference.ParseNamed(raw) 144 return err 145 } 146 147 // MarshalJSON returns the JSON bytes representation 148 func (s *SerializableNamed) MarshalJSON() ([]byte, error) { 149 return json.Marshal(s.String()) 150 }