github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/manifest/manifestlist/manifestlist.go (about)

     1  package manifestlist
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/docker/distribution"
     9  	"github.com/docker/distribution/digest"
    10  	"github.com/docker/distribution/manifest"
    11  )
    12  
    13  // MediaTypeManifestList specifies the mediaType for manifest lists.
    14  const MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json"
    15  
    16  // SchemaVersion provides a pre-initialized version structure for this
    17  // packages version of the manifest.
    18  var SchemaVersion = manifest.Versioned{
    19  	SchemaVersion: 2,
    20  	MediaType:     MediaTypeManifestList,
    21  }
    22  
    23  func init() {
    24  	manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
    25  		m := new(DeserializedManifestList)
    26  		err := m.UnmarshalJSON(b)
    27  		if err != nil {
    28  			return nil, distribution.Descriptor{}, err
    29  		}
    30  
    31  		dgst := digest.FromBytes(b)
    32  		return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err
    33  	}
    34  	err := distribution.RegisterManifestSchema(MediaTypeManifestList, manifestListFunc)
    35  	if err != nil {
    36  		panic(fmt.Sprintf("Unable to register manifest: %s", err))
    37  	}
    38  }
    39  
    40  // PlatformSpec specifies a platform where a particular image manifest is
    41  // applicable.
    42  type PlatformSpec struct {
    43  	// Architecture field specifies the CPU architecture, for example
    44  	// `amd64` or `ppc64`.
    45  	Architecture string `json:"architecture"`
    46  
    47  	// OS specifies the operating system, for example `linux` or `windows`.
    48  	OS string `json:"os"`
    49  
    50  	// Variant is an optional field specifying a variant of the CPU, for
    51  	// example `ppc64le` to specify a little-endian version of a PowerPC CPU.
    52  	Variant string `json:"variant,omitempty"`
    53  
    54  	// Features is an optional field specifuing an array of strings, each
    55  	// listing a required CPU feature (for example `sse4` or `aes`).
    56  	Features []string `json:"features,omitempty"`
    57  }
    58  
    59  // A ManifestDescriptor references a platform-specific manifest.
    60  type ManifestDescriptor struct {
    61  	distribution.Descriptor
    62  
    63  	// Platform specifies which platform the manifest pointed to by the
    64  	// descriptor runs on.
    65  	Platform PlatformSpec `json:"platform"`
    66  }
    67  
    68  // ManifestList references manifests for various platforms.
    69  type ManifestList struct {
    70  	manifest.Versioned
    71  
    72  	// Config references the image configuration as a blob.
    73  	Manifests []ManifestDescriptor `json:"manifests"`
    74  }
    75  
    76  // References returnes the distribution descriptors for the referenced image
    77  // manifests.
    78  func (m ManifestList) References() []distribution.Descriptor {
    79  	dependencies := make([]distribution.Descriptor, len(m.Manifests))
    80  	for i := range m.Manifests {
    81  		dependencies[i] = m.Manifests[i].Descriptor
    82  	}
    83  
    84  	return dependencies
    85  }
    86  
    87  // DeserializedManifestList wraps ManifestList with a copy of the original
    88  // JSON.
    89  type DeserializedManifestList struct {
    90  	ManifestList
    91  
    92  	// canonical is the canonical byte representation of the Manifest.
    93  	canonical []byte
    94  }
    95  
    96  // FromDescriptors takes a slice of descriptors, and returns a
    97  // DeserializedManifestList which contains the resulting manifest list
    98  // and its JSON representation.
    99  func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
   100  	m := ManifestList{
   101  		Versioned: SchemaVersion,
   102  	}
   103  
   104  	m.Manifests = make([]ManifestDescriptor, len(descriptors), len(descriptors))
   105  	copy(m.Manifests, descriptors)
   106  
   107  	deserialized := DeserializedManifestList{
   108  		ManifestList: m,
   109  	}
   110  
   111  	var err error
   112  	deserialized.canonical, err = json.MarshalIndent(&m, "", "   ")
   113  	return &deserialized, err
   114  }
   115  
   116  // UnmarshalJSON populates a new ManifestList struct from JSON data.
   117  func (m *DeserializedManifestList) UnmarshalJSON(b []byte) error {
   118  	m.canonical = make([]byte, len(b), len(b))
   119  	// store manifest list in canonical
   120  	copy(m.canonical, b)
   121  
   122  	// Unmarshal canonical JSON into ManifestList object
   123  	var manifestList ManifestList
   124  	if err := json.Unmarshal(m.canonical, &manifestList); err != nil {
   125  		return err
   126  	}
   127  
   128  	m.ManifestList = manifestList
   129  
   130  	return nil
   131  }
   132  
   133  // MarshalJSON returns the contents of canonical. If canonical is empty,
   134  // marshals the inner contents.
   135  func (m *DeserializedManifestList) MarshalJSON() ([]byte, error) {
   136  	if len(m.canonical) > 0 {
   137  		return m.canonical, nil
   138  	}
   139  
   140  	return nil, errors.New("JSON representation not initialized in DeserializedManifestList")
   141  }
   142  
   143  // Payload returns the raw content of the manifest list. The contents can be
   144  // used to calculate the content identifier.
   145  func (m DeserializedManifestList) Payload() (string, []byte, error) {
   146  	return m.MediaType, m.canonical, nil
   147  }