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>master
							parent
							
								
									1fc752c718
								
							
						
					
					
						commit
						3f1434525b
					
				
							
								
								
									
										9
									
								
								blobs.go
								
								
								
								
							
							
						
						
									
										9
									
								
								blobs.go
								
								
								
								
							|  | @ -192,6 +192,15 @@ type BlobCreateOption interface { | |||
| 	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.
 | ||||
| // Instances should be obtained from BlobWriteService.Writer and
 | ||||
| // BlobWriteService.Resume. If supported by the store, a writer can be
 | ||||
|  |  | |||
|  | @ -672,15 +672,6 @@ func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribut | |||
| 	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 | ||||
| 
 | ||||
| func (f optionFunc) Apply(v interface{}) error { | ||||
|  | @ -691,7 +682,7 @@ func (f optionFunc) Apply(v interface{}) error { | |||
| // mounted from the given canonical reference.
 | ||||
| func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption { | ||||
| 	return optionFunc(func(v interface{}) error { | ||||
| 		opts, ok := v.(*createOptions) | ||||
| 		opts, ok := v.(*distribution.CreateOptions) | ||||
| 		if !ok { | ||||
| 			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) { | ||||
| 	var opts createOptions | ||||
| 	var opts distribution.CreateOptions | ||||
| 
 | ||||
| 	for _, option := range options { | ||||
| 		err := option.Apply(&opts) | ||||
|  |  | |||
|  | @ -101,15 +101,6 @@ func (lbs *linkedBlobStore) Put(ctx context.Context, mediaType string, p []byte) | |||
| 	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 | ||||
| 
 | ||||
| func (f optionFunc) Apply(v interface{}) error { | ||||
|  | @ -120,7 +111,7 @@ func (f optionFunc) Apply(v interface{}) error { | |||
| // mounted from the given canonical reference.
 | ||||
| func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption { | ||||
| 	return optionFunc(func(v interface{}) error { | ||||
| 		opts, ok := v.(*createOptions) | ||||
| 		opts, ok := v.(*distribution.CreateOptions) | ||||
| 		if !ok { | ||||
| 			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) { | ||||
| 	context.GetLogger(ctx).Debug("(*linkedBlobStore).Writer") | ||||
| 
 | ||||
| 	var opts createOptions | ||||
| 	var opts distribution.CreateOptions | ||||
| 
 | ||||
| 	for _, option := range options { | ||||
| 		err := option.Apply(&opts) | ||||
|  |  | |||
|  | @ -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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in New Issue