1package distribution
2
3import (
4	"context"
5	"fmt"
6	"mime"
7
8	"github.com/opencontainers/go-digest"
9)
10
11// Manifest represents a registry object specifying a set of
12// references and an optional target
13type Manifest interface {
14	// References returns a list of objects which make up this manifest.
15	// A reference is anything which can be represented by a
16	// distribution.Descriptor. These can consist of layers, resources or other
17	// manifests.
18	//
19	// While no particular order is required, implementations should return
20	// them from highest to lowest priority. For example, one might want to
21	// return the base layer before the top layer.
22	References() []Descriptor
23
24	// Payload provides the serialized format of the manifest, in addition to
25	// the media type.
26	Payload() (mediaType string, payload []byte, err error)
27}
28
29// ManifestBuilder creates a manifest allowing one to include dependencies.
30// Instances can be obtained from a version-specific manifest package.  Manifest
31// specific data is passed into the function which creates the builder.
32type ManifestBuilder interface {
33	// Build creates the manifest from his builder.
34	Build(ctx context.Context) (Manifest, error)
35
36	// References returns a list of objects which have been added to this
37	// builder. The dependencies are returned in the order they were added,
38	// which should be from base to head.
39	References() []Descriptor
40
41	// AppendReference includes the given object in the manifest after any
42	// existing dependencies. If the add fails, such as when adding an
43	// unsupported dependency, an error may be returned.
44	//
45	// The destination of the reference is dependent on the manifest type and
46	// the dependency type.
47	AppendReference(dependency Describable) error
48}
49
50// ManifestService describes operations on image manifests.
51type ManifestService interface {
52	// Exists returns true if the manifest exists.
53	Exists(ctx context.Context, dgst digest.Digest) (bool, error)
54
55	// Get retrieves the manifest specified by the given digest
56	Get(ctx context.Context, dgst digest.Digest, options ...ManifestServiceOption) (Manifest, error)
57
58	// Put creates or updates the given manifest returning the manifest digest
59	Put(ctx context.Context, manifest Manifest, options ...ManifestServiceOption) (digest.Digest, error)
60
61	// Delete removes the manifest specified by the given digest. Deleting
62	// a manifest that doesn't exist will return ErrManifestNotFound
63	Delete(ctx context.Context, dgst digest.Digest) error
64}
65
66// ManifestEnumerator enables iterating over manifests
67type ManifestEnumerator interface {
68	// Enumerate calls ingester for each manifest.
69	Enumerate(ctx context.Context, ingester func(digest.Digest) error) error
70}
71
72// Describable is an interface for descriptors
73type Describable interface {
74	Descriptor() Descriptor
75}
76
77// ManifestMediaTypes returns the supported media types for manifests.
78func ManifestMediaTypes() (mediaTypes []string) {
79	for t := range mappings {
80		if t != "" {
81			mediaTypes = append(mediaTypes, t)
82		}
83	}
84	return
85}
86
87// UnmarshalFunc implements manifest unmarshalling a given MediaType
88type UnmarshalFunc func([]byte) (Manifest, Descriptor, error)
89
90var mappings = make(map[string]UnmarshalFunc)
91
92// UnmarshalManifest looks up manifest unmarshal functions based on
93// MediaType
94func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error) {
95	// Need to look up by the actual media type, not the raw contents of
96	// the header. Strip semicolons and anything following them.
97	var mediaType string
98	if ctHeader != "" {
99		var err error
100		mediaType, _, err = mime.ParseMediaType(ctHeader)
101		if err != nil {
102			return nil, Descriptor{}, err
103		}
104	}
105
106	unmarshalFunc, ok := mappings[mediaType]
107	if !ok {
108		unmarshalFunc, ok = mappings[""]
109		if !ok {
110			return nil, Descriptor{}, fmt.Errorf("unsupported manifest media type and no default available: %s", mediaType)
111		}
112	}
113
114	return unmarshalFunc(p)
115}
116
117// RegisterManifestSchema registers an UnmarshalFunc for a given schema type.  This
118// should be called from specific
119func RegisterManifestSchema(mediaType string, u UnmarshalFunc) error {
120	if _, ok := mappings[mediaType]; ok {
121		return fmt.Errorf("manifest media type registration would overwrite existing: %s", mediaType)
122	}
123	mappings[mediaType] = u
124	return nil
125}
126