forked from TrueCloudLab/distribution
e72294d075
Move implementation of the index from the manifestlist package to the ocischema package so that other modules making empty imports support the manifest types their authors would expect. This is a breaking change to distribution as a library but not the registry. As OCI 1.0 released the manifest and index together, that is a good package from which to initialise both manifests. The docker manifest and manifest list remain in separate packages because one was released later. The image index and manifest list still share common code in many functions not intended for import by other modules. Signed-off-by: Bracken Dawson <abdawson@gmail.com>
166 lines
5.4 KiB
Go
166 lines
5.4 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/distribution/distribution/v3"
|
|
dcontext "github.com/distribution/distribution/v3/context"
|
|
"github.com/distribution/distribution/v3/manifest"
|
|
"github.com/distribution/distribution/v3/manifest/manifestlist"
|
|
"github.com/distribution/distribution/v3/manifest/ocischema"
|
|
"github.com/distribution/distribution/v3/manifest/schema1"
|
|
"github.com/distribution/distribution/v3/manifest/schema2"
|
|
"github.com/opencontainers/go-digest"
|
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
)
|
|
|
|
// A ManifestHandler gets and puts manifests of a particular type.
|
|
type ManifestHandler interface {
|
|
// Unmarshal unmarshals the manifest from a byte slice.
|
|
Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error)
|
|
|
|
// Put creates or updates the given manifest returning the manifest digest.
|
|
Put(ctx context.Context, manifest distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error)
|
|
}
|
|
|
|
// SkipLayerVerification allows a manifest to be Put before its
|
|
// layers are on the filesystem
|
|
func SkipLayerVerification() distribution.ManifestServiceOption {
|
|
return skipLayerOption{}
|
|
}
|
|
|
|
type skipLayerOption struct{}
|
|
|
|
func (o skipLayerOption) Apply(m distribution.ManifestService) error {
|
|
if ms, ok := m.(*manifestStore); ok {
|
|
ms.skipDependencyVerification = true
|
|
return nil
|
|
}
|
|
return fmt.Errorf("skip layer verification only valid for manifestStore")
|
|
}
|
|
|
|
type manifestStore struct {
|
|
repository *repository
|
|
blobStore *linkedBlobStore
|
|
ctx context.Context
|
|
|
|
skipDependencyVerification bool
|
|
|
|
schema1Handler ManifestHandler
|
|
schema2Handler ManifestHandler
|
|
manifestListHandler ManifestHandler
|
|
ocischemaHandler ManifestHandler
|
|
ocischemaIndexHandler ManifestHandler
|
|
}
|
|
|
|
var _ distribution.ManifestService = &manifestStore{}
|
|
|
|
func (ms *manifestStore) Exists(ctx context.Context, dgst digest.Digest) (bool, error) {
|
|
dcontext.GetLogger(ms.ctx).Debug("(*manifestStore).Exists")
|
|
|
|
_, err := ms.blobStore.Stat(ms.ctx, dgst)
|
|
if err != nil {
|
|
if err == distribution.ErrBlobUnknown {
|
|
return false, nil
|
|
}
|
|
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (ms *manifestStore) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
|
|
dcontext.GetLogger(ms.ctx).Debug("(*manifestStore).Get")
|
|
|
|
// TODO(stevvooe): Need to check descriptor from above to ensure that the
|
|
// mediatype is as we expect for the manifest store.
|
|
|
|
content, err := ms.blobStore.Get(ctx, dgst)
|
|
if err != nil {
|
|
if err == distribution.ErrBlobUnknown {
|
|
return nil, distribution.ErrManifestUnknownRevision{
|
|
Name: ms.repository.Named().Name(),
|
|
Revision: dgst,
|
|
}
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
var versioned manifest.Versioned
|
|
if err = json.Unmarshal(content, &versioned); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch versioned.SchemaVersion {
|
|
case 1:
|
|
return ms.schema1Handler.Unmarshal(ctx, dgst, content)
|
|
case 2:
|
|
// This can be an image manifest or a manifest list
|
|
switch versioned.MediaType {
|
|
case schema2.MediaTypeManifest:
|
|
return ms.schema2Handler.Unmarshal(ctx, dgst, content)
|
|
case v1.MediaTypeImageManifest:
|
|
return ms.ocischemaHandler.Unmarshal(ctx, dgst, content)
|
|
case manifestlist.MediaTypeManifestList:
|
|
return ms.manifestListHandler.Unmarshal(ctx, dgst, content)
|
|
case v1.MediaTypeImageIndex:
|
|
return ms.ocischemaIndexHandler.Unmarshal(ctx, dgst, content)
|
|
case "":
|
|
// OCI image or image index - no media type in the content
|
|
|
|
// First see if it looks like an image index
|
|
res, err := ms.ocischemaIndexHandler.Unmarshal(ctx, dgst, content)
|
|
resIndex := res.(*ocischema.DeserializedImageIndex)
|
|
if err == nil && resIndex.Manifests != nil {
|
|
return resIndex, nil
|
|
}
|
|
|
|
// Otherwise, assume it must be an image manifest
|
|
return ms.ocischemaHandler.Unmarshal(ctx, dgst, content)
|
|
default:
|
|
return nil, distribution.ErrManifestVerification{fmt.Errorf("unrecognized manifest content type %s", versioned.MediaType)}
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("unrecognized manifest schema version %d", versioned.SchemaVersion)
|
|
}
|
|
|
|
func (ms *manifestStore) Put(ctx context.Context, manifest distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
|
|
dcontext.GetLogger(ms.ctx).Debug("(*manifestStore).Put")
|
|
|
|
switch manifest.(type) {
|
|
case *schema1.SignedManifest:
|
|
return ms.schema1Handler.Put(ctx, manifest, ms.skipDependencyVerification)
|
|
case *schema2.DeserializedManifest:
|
|
return ms.schema2Handler.Put(ctx, manifest, ms.skipDependencyVerification)
|
|
case *ocischema.DeserializedManifest:
|
|
return ms.ocischemaHandler.Put(ctx, manifest, ms.skipDependencyVerification)
|
|
case *manifestlist.DeserializedManifestList:
|
|
return ms.manifestListHandler.Put(ctx, manifest, ms.skipDependencyVerification)
|
|
case *ocischema.DeserializedImageIndex:
|
|
return ms.ocischemaIndexHandler.Put(ctx, manifest, ms.skipDependencyVerification)
|
|
}
|
|
|
|
return "", fmt.Errorf("unrecognized manifest type %T", manifest)
|
|
}
|
|
|
|
// Delete removes the revision of the specified manifest.
|
|
func (ms *manifestStore) Delete(ctx context.Context, dgst digest.Digest) error {
|
|
dcontext.GetLogger(ms.ctx).Debug("(*manifestStore).Delete")
|
|
return ms.blobStore.Delete(ctx, dgst)
|
|
}
|
|
|
|
func (ms *manifestStore) Enumerate(ctx context.Context, ingester func(digest.Digest) error) error {
|
|
err := ms.blobStore.Enumerate(ctx, func(dgst digest.Digest) error {
|
|
err := ingester(dgst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
return err
|
|
}
|