...

Source file src/github.com/docker/distribution/manifest/ocischema/manifest.go

Documentation: github.com/docker/distribution/manifest/ocischema

     1  package ocischema
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/docker/distribution"
     9  	"github.com/docker/distribution/manifest"
    10  	"github.com/opencontainers/go-digest"
    11  	v1 "github.com/opencontainers/image-spec/specs-go/v1"
    12  )
    13  
    14  var (
    15  	// SchemaVersion provides a pre-initialized version structure for this
    16  	// packages version of the manifest.
    17  	SchemaVersion = manifest.Versioned{
    18  		SchemaVersion: 2, // historical value here.. does not pertain to OCI or docker version
    19  		MediaType:     v1.MediaTypeImageManifest,
    20  	}
    21  )
    22  
    23  func init() {
    24  	ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
    25  		if err := validateManifest(b); err != nil {
    26  			return nil, distribution.Descriptor{}, err
    27  		}
    28  		m := new(DeserializedManifest)
    29  		err := m.UnmarshalJSON(b)
    30  		if err != nil {
    31  			return nil, distribution.Descriptor{}, err
    32  		}
    33  
    34  		dgst := digest.FromBytes(b)
    35  		return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageManifest}, err
    36  	}
    37  	err := distribution.RegisterManifestSchema(v1.MediaTypeImageManifest, ocischemaFunc)
    38  	if err != nil {
    39  		panic(fmt.Sprintf("Unable to register manifest: %s", err))
    40  	}
    41  }
    42  
    43  // Manifest defines a ocischema manifest.
    44  type Manifest struct {
    45  	manifest.Versioned
    46  
    47  	// Config references the image configuration as a blob.
    48  	Config distribution.Descriptor `json:"config"`
    49  
    50  	// Layers lists descriptors for the layers referenced by the
    51  	// configuration.
    52  	Layers []distribution.Descriptor `json:"layers"`
    53  
    54  	// Annotations contains arbitrary metadata for the image manifest.
    55  	Annotations map[string]string `json:"annotations,omitempty"`
    56  }
    57  
    58  // References returns the descriptors of this manifests references.
    59  func (m Manifest) References() []distribution.Descriptor {
    60  	references := make([]distribution.Descriptor, 0, 1+len(m.Layers))
    61  	references = append(references, m.Config)
    62  	references = append(references, m.Layers...)
    63  	return references
    64  }
    65  
    66  // Target returns the target of this manifest.
    67  func (m Manifest) Target() distribution.Descriptor {
    68  	return m.Config
    69  }
    70  
    71  // DeserializedManifest wraps Manifest with a copy of the original JSON.
    72  // It satisfies the distribution.Manifest interface.
    73  type DeserializedManifest struct {
    74  	Manifest
    75  
    76  	// canonical is the canonical byte representation of the Manifest.
    77  	canonical []byte
    78  }
    79  
    80  // FromStruct takes a Manifest structure, marshals it to JSON, and returns a
    81  // DeserializedManifest which contains the manifest and its JSON representation.
    82  func FromStruct(m Manifest) (*DeserializedManifest, error) {
    83  	var deserialized DeserializedManifest
    84  	deserialized.Manifest = m
    85  
    86  	var err error
    87  	deserialized.canonical, err = json.MarshalIndent(&m, "", "   ")
    88  	return &deserialized, err
    89  }
    90  
    91  // UnmarshalJSON populates a new Manifest struct from JSON data.
    92  func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
    93  	m.canonical = make([]byte, len(b))
    94  	// store manifest in canonical
    95  	copy(m.canonical, b)
    96  
    97  	// Unmarshal canonical JSON into Manifest object
    98  	var manifest Manifest
    99  	if err := json.Unmarshal(m.canonical, &manifest); err != nil {
   100  		return err
   101  	}
   102  
   103  	if manifest.MediaType != "" && manifest.MediaType != v1.MediaTypeImageManifest {
   104  		return fmt.Errorf("if present, mediaType in manifest should be '%s' not '%s'",
   105  			v1.MediaTypeImageManifest, manifest.MediaType)
   106  	}
   107  
   108  	m.Manifest = manifest
   109  
   110  	return nil
   111  }
   112  
   113  // MarshalJSON returns the contents of canonical. If canonical is empty,
   114  // marshals the inner contents.
   115  func (m *DeserializedManifest) MarshalJSON() ([]byte, error) {
   116  	if len(m.canonical) > 0 {
   117  		return m.canonical, nil
   118  	}
   119  
   120  	return nil, errors.New("JSON representation not initialized in DeserializedManifest")
   121  }
   122  
   123  // Payload returns the raw content of the manifest. The contents can be used to
   124  // calculate the content identifier.
   125  func (m DeserializedManifest) Payload() (string, []byte, error) {
   126  	return v1.MediaTypeImageManifest, m.canonical, nil
   127  }
   128  
   129  // unknownDocument represents a manifest, manifest list, or index that has not
   130  // yet been validated
   131  type unknownDocument struct {
   132  	Manifests interface{} `json:"manifests,omitempty"`
   133  }
   134  
   135  // validateManifest returns an error if the byte slice is invalid JSON or if it
   136  // contains fields that belong to a index
   137  func validateManifest(b []byte) error {
   138  	var doc unknownDocument
   139  	if err := json.Unmarshal(b, &doc); err != nil {
   140  		return err
   141  	}
   142  	if doc.Manifests != nil {
   143  		return errors.New("ocimanifest: expected manifest but found index")
   144  	}
   145  	return nil
   146  }
   147  

View as plain text