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  }