Export storage.CreateOptions in top-level package

Let the options for `BlobStore.Create()` be modified in middleware
wrappers.

Signed-off-by: Michal Minar <miminar@redhat.com>
This commit is contained in:
Michal Minář 2016-07-06 16:00:29 +02:00 committed by Michal Minar
parent 1fc752c718
commit 3f1434525b
4 changed files with 91 additions and 22 deletions

View File

@ -192,6 +192,15 @@ type BlobCreateOption interface {
Apply(interface{}) error Apply(interface{}) error
} }
// CreateOptions is a collection of blob creation modifiers relevant to general
// blob storage intended to be configured by the BlobCreateOption.Apply method.
type CreateOptions struct {
Mount struct {
ShouldMount bool
From reference.Canonical
}
}
// BlobWriter provides a handle for inserting data into a blob store. // BlobWriter provides a handle for inserting data into a blob store.
// Instances should be obtained from BlobWriteService.Writer and // Instances should be obtained from BlobWriteService.Writer and
// BlobWriteService.Resume. If supported by the store, a writer can be // BlobWriteService.Resume. If supported by the store, a writer can be

View File

@ -672,15 +672,6 @@ func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribut
return writer.Commit(ctx, desc) return writer.Commit(ctx, desc)
} }
// createOptions is a collection of blob creation modifiers relevant to general
// blob storage intended to be configured by the BlobCreateOption.Apply method.
type createOptions struct {
Mount struct {
ShouldMount bool
From reference.Canonical
}
}
type optionFunc func(interface{}) error type optionFunc func(interface{}) error
func (f optionFunc) Apply(v interface{}) error { func (f optionFunc) Apply(v interface{}) error {
@ -691,7 +682,7 @@ func (f optionFunc) Apply(v interface{}) error {
// mounted from the given canonical reference. // mounted from the given canonical reference.
func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption { func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption {
return optionFunc(func(v interface{}) error { return optionFunc(func(v interface{}) error {
opts, ok := v.(*createOptions) opts, ok := v.(*distribution.CreateOptions)
if !ok { if !ok {
return fmt.Errorf("unexpected options type: %T", v) return fmt.Errorf("unexpected options type: %T", v)
} }
@ -704,7 +695,7 @@ func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption {
} }
func (bs *blobs) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) { func (bs *blobs) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
var opts createOptions var opts distribution.CreateOptions
for _, option := range options { for _, option := range options {
err := option.Apply(&opts) err := option.Apply(&opts)

View File

@ -101,15 +101,6 @@ func (lbs *linkedBlobStore) Put(ctx context.Context, mediaType string, p []byte)
return desc, lbs.linkBlob(ctx, desc) return desc, lbs.linkBlob(ctx, desc)
} }
// createOptions is a collection of blob creation modifiers relevant to general
// blob storage intended to be configured by the BlobCreateOption.Apply method.
type createOptions struct {
Mount struct {
ShouldMount bool
From reference.Canonical
}
}
type optionFunc func(interface{}) error type optionFunc func(interface{}) error
func (f optionFunc) Apply(v interface{}) error { func (f optionFunc) Apply(v interface{}) error {
@ -120,7 +111,7 @@ func (f optionFunc) Apply(v interface{}) error {
// mounted from the given canonical reference. // mounted from the given canonical reference.
func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption { func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption {
return optionFunc(func(v interface{}) error { return optionFunc(func(v interface{}) error {
opts, ok := v.(*createOptions) opts, ok := v.(*distribution.CreateOptions)
if !ok { if !ok {
return fmt.Errorf("unexpected options type: %T", v) return fmt.Errorf("unexpected options type: %T", v)
} }
@ -136,7 +127,7 @@ func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption {
func (lbs *linkedBlobStore) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) { func (lbs *linkedBlobStore) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
context.GetLogger(ctx).Debug("(*linkedBlobStore).Writer") context.GetLogger(ctx).Debug("(*linkedBlobStore).Writer")
var opts createOptions var opts distribution.CreateOptions
for _, option := range options { for _, option := range options {
err := option.Apply(&opts) err := option.Apply(&opts)

View File

@ -0,0 +1,78 @@
package storage
import (
"io"
"testing"
"github.com/docker/distribution"
"github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/testutil"
)
func TestLinkedBlobStoreCreateWithMountFrom(t *testing.T) {
fooRepoName, _ := reference.ParseNamed("nm/foo")
fooEnv := newManifestStoreTestEnv(t, fooRepoName, "thetag")
ctx := context.Background()
// Build up some test layers and add them to the manifest, saving the
// readseekers for upload later.
testLayers := map[digest.Digest]io.ReadSeeker{}
for i := 0; i < 2; i++ {
rs, ds, err := testutil.CreateRandomTarFile()
if err != nil {
t.Fatalf("unexpected error generating test layer file")
}
dgst := digest.Digest(ds)
testLayers[digest.Digest(dgst)] = rs
}
// upload the layers to foo/bar
for dgst, rs := range testLayers {
wr, err := fooEnv.repository.Blobs(fooEnv.ctx).Create(fooEnv.ctx)
if err != nil {
t.Fatalf("unexpected error creating test upload: %v", err)
}
if _, err := io.Copy(wr, rs); err != nil {
t.Fatalf("unexpected error copying to upload: %v", err)
}
if _, err := wr.Commit(fooEnv.ctx, distribution.Descriptor{Digest: dgst}); err != nil {
t.Fatalf("unexpected error finishing upload: %v", err)
}
}
// create another repository nm/bar
barRepoName, _ := reference.ParseNamed("nm/bar")
barRepo, err := fooEnv.registry.Repository(ctx, barRepoName)
if err != nil {
t.Fatalf("unexpected error getting repo: %v", err)
}
// cross-repo mount the test layers into a nm/bar
for dgst := range testLayers {
fooCanonical, _ := reference.WithDigest(fooRepoName, dgst)
option := WithMountFrom(fooCanonical)
// ensure we can instrospect it
createOpts := distribution.CreateOptions{}
if err := option.Apply(&createOpts); err != nil {
t.Fatalf("failed to apply MountFrom option: %v", err)
}
if !createOpts.Mount.ShouldMount || createOpts.Mount.From.String() != fooCanonical.String() {
t.Fatalf("unexpected create options: %#+v", createOpts.Mount)
}
_, err := barRepo.Blobs(ctx).Create(ctx, WithMountFrom(fooCanonical))
if err == nil {
t.Fatalf("unexpected non-error while mounting from %q: %v", fooRepoName.String(), err)
}
if _, ok := err.(distribution.ErrBlobMounted); !ok {
t.Fatalf("expected ErrMountFrom error, not %T: %v", err, err)
}
}
}