c40c4b289a
Enable configuration options that can selectively disable validation that dependencies exist within the registry before the image index is uploaded. This enables sparse indexes, where a registry holds a manifest index that could be signed (so the digest must not change) but does not hold every referenced image in the index. The use case for this is when a registry mirror does not need to mirror all platforms, but does need to maintain the digests of all manifests either because they are signed or because they are pulled by digest. The registry administrator can also select specific image architectures that must exist in the registry, enabling a registry operator to select only the platforms they care about and ensure all image indexes uploaded to the registry are valid for those platforms. Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
125 lines
4.2 KiB
Go
125 lines
4.2 KiB
Go
package distribution
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"mime"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
// Manifest represents a registry object specifying a set of
|
|
// references and an optional target
|
|
type Manifest interface {
|
|
// References returns a list of objects which make up this manifest.
|
|
// A reference is anything which can be represented by a
|
|
// distribution.Descriptor. These can consist of layers, resources or other
|
|
// manifests.
|
|
//
|
|
// While no particular order is required, implementations should return
|
|
// them from highest to lowest priority. For example, one might want to
|
|
// return the base layer before the top layer.
|
|
References() []Descriptor
|
|
|
|
// Payload provides the serialized format of the manifest, in addition to
|
|
// the media type.
|
|
Payload() (mediaType string, payload []byte, err error)
|
|
}
|
|
|
|
// ManifestBuilder creates a manifest allowing one to include dependencies.
|
|
// Instances can be obtained from a version-specific manifest package. Manifest
|
|
// specific data is passed into the function which creates the builder.
|
|
type ManifestBuilder interface {
|
|
// Build creates the manifest from his builder.
|
|
Build(ctx context.Context) (Manifest, error)
|
|
|
|
// References returns a list of objects which have been added to this
|
|
// builder. The dependencies are returned in the order they were added,
|
|
// which should be from base to head.
|
|
References() []Descriptor
|
|
|
|
// AppendReference includes the given object in the manifest after any
|
|
// existing dependencies. If the add fails, such as when adding an
|
|
// unsupported dependency, an error may be returned.
|
|
//
|
|
// The destination of the reference is dependent on the manifest type and
|
|
// the dependency type.
|
|
AppendReference(dependency Describable) error
|
|
}
|
|
|
|
// ManifestService describes operations on manifests.
|
|
type ManifestService interface {
|
|
// Exists returns true if the manifest exists.
|
|
Exists(ctx context.Context, dgst digest.Digest) (bool, error)
|
|
|
|
// Get retrieves the manifest specified by the given digest
|
|
Get(ctx context.Context, dgst digest.Digest, options ...ManifestServiceOption) (Manifest, error)
|
|
|
|
// Put creates or updates the given manifest returning the manifest digest
|
|
Put(ctx context.Context, manifest Manifest, options ...ManifestServiceOption) (digest.Digest, error)
|
|
|
|
// Delete removes the manifest specified by the given digest. Deleting
|
|
// a manifest that doesn't exist will return ErrManifestNotFound
|
|
Delete(ctx context.Context, dgst digest.Digest) error
|
|
}
|
|
|
|
// ManifestEnumerator enables iterating over manifests
|
|
type ManifestEnumerator interface {
|
|
// Enumerate calls ingester for each manifest.
|
|
Enumerate(ctx context.Context, ingester func(digest.Digest) error) error
|
|
}
|
|
|
|
// Describable is an interface for descriptors
|
|
type Describable interface {
|
|
Descriptor() Descriptor
|
|
}
|
|
|
|
// ManifestMediaTypes returns the supported media types for manifests.
|
|
func ManifestMediaTypes() (mediaTypes []string) {
|
|
for t := range mappings {
|
|
if t != "" {
|
|
mediaTypes = append(mediaTypes, t)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// UnmarshalFunc implements manifest unmarshalling a given MediaType
|
|
type UnmarshalFunc func([]byte) (Manifest, Descriptor, error)
|
|
|
|
var mappings = make(map[string]UnmarshalFunc)
|
|
|
|
// UnmarshalManifest looks up manifest unmarshal functions based on
|
|
// MediaType
|
|
func UnmarshalManifest(ctHeader string, p []byte) (Manifest, Descriptor, error) {
|
|
// Need to look up by the actual media type, not the raw contents of
|
|
// the header. Strip semicolons and anything following them.
|
|
var mediaType string
|
|
if ctHeader != "" {
|
|
var err error
|
|
mediaType, _, err = mime.ParseMediaType(ctHeader)
|
|
if err != nil {
|
|
return nil, Descriptor{}, err
|
|
}
|
|
}
|
|
|
|
unmarshalFunc, ok := mappings[mediaType]
|
|
if !ok {
|
|
unmarshalFunc, ok = mappings[""]
|
|
if !ok {
|
|
return nil, Descriptor{}, fmt.Errorf("unsupported manifest media type and no default available: %s", mediaType)
|
|
}
|
|
}
|
|
|
|
return unmarshalFunc(p)
|
|
}
|
|
|
|
// RegisterManifestSchema registers an UnmarshalFunc for a given schema type. This
|
|
// should be called from specific
|
|
func RegisterManifestSchema(mediaType string, u UnmarshalFunc) error {
|
|
if _, ok := mappings[mediaType]; ok {
|
|
return fmt.Errorf("manifest media type registration would overwrite existing: %s", mediaType)
|
|
}
|
|
mappings[mediaType] = u
|
|
return nil
|
|
}
|