From f331da2daa8473904120d6515b222e38a40f1ccd Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 15 Jun 2015 10:39:34 -0700 Subject: [PATCH] Allow Manifest Service to be configured with function arguments Signed-off-by: Richard Scothern --- notifications/listener.go | 12 +++++--- notifications/listener_test.go | 7 +++-- registry.go | 3 +- registry/client/repository.go | 10 +++++-- registry/client/repository_test.go | 36 ++++++++++++++++++++---- registry/handlers/images.go | 19 ++++++++----- registry/handlers/tags.go | 6 +++- registry/storage/manifeststore.go | 39 +++++++++++++++++--------- registry/storage/manifeststore_test.go | 12 +++++--- registry/storage/registry.go | 22 +++++++++++---- 10 files changed, 118 insertions(+), 48 deletions(-) diff --git a/notifications/listener.go b/notifications/listener.go index 7b3e6b21..b86fa8a4 100644 --- a/notifications/listener.go +++ b/notifications/listener.go @@ -51,11 +51,15 @@ func Listen(repo distribution.Repository, listener Listener) distribution.Reposi } } -func (rl *repositoryListener) Manifests() distribution.ManifestService { - return &manifestServiceListener{ - ManifestService: rl.Repository.Manifests(), - parent: rl, +func (rl *repositoryListener) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) { + manifests, err := rl.Repository.Manifests(ctx, options...) + if err != nil { + return nil, err } + return &manifestServiceListener{ + ManifestService: manifests, + parent: rl, + }, nil } func (rl *repositoryListener) Blobs(ctx context.Context) distribution.BlobStore { diff --git a/notifications/listener_test.go b/notifications/listener_test.go index 5b0250b4..49629147 100644 --- a/notifications/listener_test.go +++ b/notifications/listener_test.go @@ -146,9 +146,12 @@ func checkExerciseRepository(t *testing.T, repository distribution.Repository) { t.Fatalf("unexpected error signing manifest: %v", err) } - manifests := repository.Manifests() + manifests, err := repository.Manifests(ctx) + if err != nil { + t.Fatal(err.Error()) + } - if err := manifests.Put(sm); err != nil { + if err = manifests.Put(sm); err != nil { t.Fatalf("unexpected error putting the manifest: %v", err) } diff --git a/registry.go b/registry.go index 85940824..763a189b 100644 --- a/registry.go +++ b/registry.go @@ -46,7 +46,8 @@ type Repository interface { Name() string // Manifests returns a reference to this repository's manifest service. - Manifests() ManifestService + // with the supplied options applied. + Manifests(ctx context.Context, options ...ManifestServiceOption) (ManifestService, error) // Blobs returns a reference to this repository's blob service. Blobs(ctx context.Context) BlobStore diff --git a/registry/client/repository.go b/registry/client/repository.go index 1f360ec8..840a7af6 100644 --- a/registry/client/repository.go +++ b/registry/client/repository.go @@ -70,18 +70,20 @@ func (r *repository) Blobs(ctx context.Context) distribution.BlobStore { } } -func (r *repository) Manifests() distribution.ManifestService { +func (r *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) { + // todo(richardscothern): options should be sent over the wire return &manifests{ name: r.Name(), ub: r.ub, client: r.client, etags: make(map[string]string), - } + }, nil } func (r *repository) Signatures() distribution.SignatureService { + ms, _ := r.Manifests(r.context) return &signatures{ - manifests: r.Manifests(), + manifests: ms, } } @@ -236,6 +238,8 @@ func (ms *manifests) Put(m *manifest.SignedManifest) error { return err } + // todo(richardscothern): do something with options here when they become applicable + putRequest, err := http.NewRequest("PUT", manifestURL, bytes.NewReader(m.Raw)) if err != nil { return err diff --git a/registry/client/repository_test.go b/registry/client/repository_test.go index bf02ce27..642ef998 100644 --- a/registry/client/repository_test.go +++ b/registry/client/repository_test.go @@ -492,6 +492,7 @@ func checkEqualManifest(m1, m2 *manifest.SignedManifest) error { } func TestManifestFetch(t *testing.T) { + ctx := context.Background() repo := "test.example.com/repo" m1, dgst := newRandomSchemaV1Manifest(repo, "latest", 6) var m testutil.RequestResponseMap @@ -504,7 +505,10 @@ func TestManifestFetch(t *testing.T) { if err != nil { t.Fatal(err) } - ms := r.Manifests() + ms, err := r.Manifests(ctx) + if err != nil { + t.Fatal(err) + } ok, err := ms.Exists(dgst) if err != nil { @@ -536,8 +540,12 @@ func TestManifestFetchWithEtag(t *testing.T) { if err != nil { t.Fatal(err) } + ctx := context.Background() + ms, err := r.Manifests(ctx) + if err != nil { + t.Fatal(err) + } - ms := r.Manifests() m2, err := ms.GetByTag("latest", AddEtagToTag("latest", d1.String())) if err != nil { t.Fatal(err) @@ -572,8 +580,12 @@ func TestManifestDelete(t *testing.T) { if err != nil { t.Fatal(err) } + ctx := context.Background() + ms, err := r.Manifests(ctx) + if err != nil { + t.Fatal(err) + } - ms := r.Manifests() if err := ms.Delete(dgst1); err != nil { t.Fatal(err) } @@ -609,8 +621,12 @@ func TestManifestPut(t *testing.T) { if err != nil { t.Fatal(err) } + ctx := context.Background() + ms, err := r.Manifests(ctx) + if err != nil { + t.Fatal(err) + } - ms := r.Manifests() if err := ms.Put(m1); err != nil { t.Fatal(err) } @@ -653,8 +669,12 @@ func TestManifestTags(t *testing.T) { if err != nil { t.Fatal(err) } + ctx := context.Background() + ms, err := r.Manifests(ctx) + if err != nil { + t.Fatal(err) + } - ms := r.Manifests() tags, err := ms.Tags() if err != nil { t.Fatal(err) @@ -691,7 +711,11 @@ func TestManifestUnauthorized(t *testing.T) { if err != nil { t.Fatal(err) } - ms := r.Manifests() + ctx := context.Background() + ms, err := r.Manifests(ctx) + if err != nil { + t.Fatal(err) + } _, err = ms.Get(dgst) if err == nil { diff --git a/registry/handlers/images.go b/registry/handlers/images.go index 747b2780..e5b0bc77 100644 --- a/registry/handlers/images.go +++ b/registry/handlers/images.go @@ -50,13 +50,13 @@ type imageManifestHandler struct { // GetImageManifest fetches the image manifest from the storage backend, if it exists. func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http.Request) { ctxu.GetLogger(imh).Debug("GetImageManifest") - manifests := imh.Repository.Manifests() - - var ( - sm *manifest.SignedManifest - err error - ) + manifests, err := imh.Repository.Manifests(imh) + if err != nil { + imh.Errors = append(imh.Errors, err) + return + } + var sm *manifest.SignedManifest if imh.Tag != "" { sm, err = manifests.GetByTag(imh.Tag) } else { @@ -106,7 +106,12 @@ func etagMatch(r *http.Request, etag string) bool { // PutImageManifest validates and stores and image in the registry. func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request) { ctxu.GetLogger(imh).Debug("PutImageManifest") - manifests := imh.Repository.Manifests() + manifests, err := imh.Repository.Manifests(imh) + if err != nil { + imh.Errors = append(imh.Errors, err) + return + } + dec := json.NewDecoder(r.Body) var manifest manifest.SignedManifest diff --git a/registry/handlers/tags.go b/registry/handlers/tags.go index 00f9760e..54725585 100644 --- a/registry/handlers/tags.go +++ b/registry/handlers/tags.go @@ -34,7 +34,11 @@ type tagsAPIResponse struct { // GetTags returns a json list of tags for a specific image name. func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - manifests := th.Repository.Manifests() + manifests, err := th.Repository.Manifests(th) + if err != nil { + th.Errors = append(th.Errors, err) + return + } tags, err := manifests.Tags() if err != nil { diff --git a/registry/storage/manifeststore.go b/registry/storage/manifeststore.go index 8f6c3562..27d6a9fa 100644 --- a/registry/storage/manifeststore.go +++ b/registry/storage/manifeststore.go @@ -11,10 +11,11 @@ import ( ) type manifestStore struct { - repository *repository - revisionStore *revisionStore - tagStore *tagStore - ctx context.Context + repository *repository + revisionStore *revisionStore + tagStore *tagStore + ctx context.Context + skipDependencyVerification bool } var _ distribution.ManifestService = &manifestStore{} @@ -39,10 +40,19 @@ func (ms *manifestStore) Get(dgst digest.Digest) (*manifest.SignedManifest, erro return ms.revisionStore.get(ms.ctx, dgst) } +// SkipLayerVerification allows a manifest to be Put before it's +// layers are on the filesystem +func SkipLayerVerification(ms distribution.ManifestService) error { + if ms, ok := ms.(*manifestStore); ok { + ms.skipDependencyVerification = true + return nil + } + return fmt.Errorf("skip layer verification only valid for manifeststore") +} + func (ms *manifestStore) Put(manifest *manifest.SignedManifest) error { context.GetLogger(ms.ctx).Debug("(*manifestStore).Put") - // Verify the manifest. if err := ms.verifyManifest(ms.ctx, manifest); err != nil { return err } @@ -113,18 +123,19 @@ func (ms *manifestStore) verifyManifest(ctx context.Context, mnfst *manifest.Sig } } - for _, fsLayer := range mnfst.FSLayers { - _, err := ms.repository.Blobs(ctx).Stat(ctx, fsLayer.BlobSum) - if err != nil { - if err != distribution.ErrBlobUnknown { - errs = append(errs, err) - } + if !ms.skipDependencyVerification { + for _, fsLayer := range mnfst.FSLayers { + _, err := ms.repository.Blobs(ctx).Stat(ctx, fsLayer.BlobSum) + if err != nil { + if err != distribution.ErrBlobUnknown { + errs = append(errs, err) + } - // On error here, we always append unknown blob errors. - errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: fsLayer.BlobSum}) + // On error here, we always append unknown blob errors. + errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: fsLayer.BlobSum}) + } } } - if len(errs) != 0 { return errs } diff --git a/registry/storage/manifeststore_test.go b/registry/storage/manifeststore_test.go index 3422985a..55ea80ac 100644 --- a/registry/storage/manifeststore_test.go +++ b/registry/storage/manifeststore_test.go @@ -48,7 +48,11 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE func TestManifestStorage(t *testing.T) { env := newManifestStoreTestEnv(t, "foo/bar", "thetag") - ms := env.repository.Manifests() + ctx := context.Background() + ms, err := env.repository.Manifests(ctx) + if err != nil { + t.Fatal(err) + } exists, err := ms.ExistsByTag(env.tag) if err != nil { @@ -97,14 +101,14 @@ func TestManifestStorage(t *testing.T) { t.Fatalf("unexpected error generating private key: %v", err) } - sm, err := manifest.Sign(&m, pk) - if err != nil { + sm, merr := manifest.Sign(&m, pk) + if merr != nil { t.Fatalf("error signing manifest: %v", err) } err = ms.Put(sm) if err == nil { - t.Fatalf("expected errors putting manifest") + t.Fatalf("expected errors putting manifest with full verification") } switch err := err.(type) { diff --git a/registry/storage/registry.go b/registry/storage/registry.go index ff33f410..cf0fe3e7 100644 --- a/registry/storage/registry.go +++ b/registry/storage/registry.go @@ -99,15 +99,15 @@ func (repo *repository) Name() string { // Manifests returns an instance of ManifestService. Instantiation is cheap and // may be context sensitive in the future. The instance should be used similar // to a request local. -func (repo *repository) Manifests() distribution.ManifestService { - return &manifestStore{ - ctx: repo.ctx, +func (repo *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) { + ms := &manifestStore{ + ctx: ctx, repository: repo, revisionStore: &revisionStore{ - ctx: repo.ctx, + ctx: ctx, repository: repo, blobStore: &linkedBlobStore{ - ctx: repo.ctx, + ctx: ctx, blobStore: repo.blobStore, repository: repo, statter: &linkedBlobStatter{ @@ -122,11 +122,21 @@ func (repo *repository) Manifests() distribution.ManifestService { }, }, tagStore: &tagStore{ - ctx: repo.ctx, + ctx: ctx, repository: repo, blobStore: repo.registry.blobStore, }, } + + // Apply options + for _, option := range options { + err := option(ms) + if err != nil { + return nil, err + } + } + + return ms, nil } // Blobs returns an instance of the BlobStore. Instantiation is cheap and