1package storage
2
3import (
4	"context"
5	"fmt"
6
7	"github.com/docker/distribution"
8	dcontext "github.com/docker/distribution/context"
9	"github.com/docker/distribution/manifest/manifestlist"
10	"github.com/opencontainers/go-digest"
11)
12
13// manifestListHandler is a ManifestHandler that covers schema2 manifest lists.
14type manifestListHandler struct {
15	repository distribution.Repository
16	blobStore  distribution.BlobStore
17	ctx        context.Context
18}
19
20var _ ManifestHandler = &manifestListHandler{}
21
22func (ms *manifestListHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) {
23	dcontext.GetLogger(ms.ctx).Debug("(*manifestListHandler).Unmarshal")
24
25	m := &manifestlist.DeserializedManifestList{}
26	if err := m.UnmarshalJSON(content); err != nil {
27		return nil, err
28	}
29
30	return m, nil
31}
32
33func (ms *manifestListHandler) Put(ctx context.Context, manifestList distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error) {
34	dcontext.GetLogger(ms.ctx).Debug("(*manifestListHandler).Put")
35
36	m, ok := manifestList.(*manifestlist.DeserializedManifestList)
37	if !ok {
38		return "", fmt.Errorf("wrong type put to manifestListHandler: %T", manifestList)
39	}
40
41	if err := ms.verifyManifest(ms.ctx, *m, skipDependencyVerification); err != nil {
42		return "", err
43	}
44
45	mt, payload, err := m.Payload()
46	if err != nil {
47		return "", err
48	}
49
50	revision, err := ms.blobStore.Put(ctx, mt, payload)
51	if err != nil {
52		dcontext.GetLogger(ctx).Errorf("error putting payload into blobstore: %v", err)
53		return "", err
54	}
55
56	return revision.Digest, nil
57}
58
59// verifyManifest ensures that the manifest content is valid from the
60// perspective of the registry. As a policy, the registry only tries to
61// store valid content, leaving trust policies of that content up to
62// consumers.
63func (ms *manifestListHandler) verifyManifest(ctx context.Context, mnfst manifestlist.DeserializedManifestList, skipDependencyVerification bool) error {
64	var errs distribution.ErrManifestVerification
65
66	if mnfst.SchemaVersion != 2 {
67		return fmt.Errorf("unrecognized manifest list schema version %d", mnfst.SchemaVersion)
68	}
69
70	if !skipDependencyVerification {
71		// This manifest service is different from the blob service
72		// returned by Blob. It uses a linked blob store to ensure that
73		// only manifests are accessible.
74
75		manifestService, err := ms.repository.Manifests(ctx)
76		if err != nil {
77			return err
78		}
79
80		for _, manifestDescriptor := range mnfst.References() {
81			exists, err := manifestService.Exists(ctx, manifestDescriptor.Digest)
82			if err != nil && err != distribution.ErrBlobUnknown {
83				errs = append(errs, err)
84			}
85			if err != nil || !exists {
86				// On error here, we always append unknown blob errors.
87				errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: manifestDescriptor.Digest})
88			}
89		}
90	}
91	if len(errs) != 0 {
92		return errs
93	}
94
95	return nil
96}
97