9c88801a12
Back in the before time, the best practices surrounding usage of Context weren't quite worked out. We defined our own type to make usage easier. As this packaged was used elsewhere, it make it more and more challenging to integrate with the forked `Context` type. Now that it is available in the standard library, we can just use that one directly. To make usage more consistent, we now use `dcontext` when referring to the distribution context package. Signed-off-by: Stephen J Day <stephen.day@docker.com>
143 lines
4.3 KiB
Go
143 lines
4.3 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/docker/distribution"
|
|
dcontext "github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/manifest"
|
|
"github.com/docker/distribution/manifest/manifestlist"
|
|
"github.com/docker/distribution/manifest/schema1"
|
|
"github.com/docker/distribution/manifest/schema2"
|
|
"github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
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 manifestlist.MediaTypeManifestList:
|
|
return ms.manifestListHandler.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 *manifestlist.DeserializedManifestList:
|
|
return ms.manifestListHandler.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
|
|
}
|