Use reference package internally

Most places in the registry were using string types to refer to
repository names. This changes them to use reference.Named, so the type
system can enforce validation of the naming rules.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2015-12-15 14:35:23 -08:00
parent caa2001e1f
commit 4441333912
35 changed files with 323 additions and 246 deletions

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/reference"
"github.com/docker/libtrust" "github.com/docker/libtrust"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
@ -39,10 +40,8 @@ type configManifestBuilder struct {
// configJSON is configuration supplied when the ManifestBuilder was // configJSON is configuration supplied when the ManifestBuilder was
// created. // created.
configJSON []byte configJSON []byte
// name is the name provided to NewConfigManifestBuilder // ref contains the name and optional tag provided to NewConfigManifestBuilder.
name string ref reference.Named
// tag is the tag provided to NewConfigManifestBuilder
tag string
// descriptors is the set of descriptors referencing the layers. // descriptors is the set of descriptors referencing the layers.
descriptors []distribution.Descriptor descriptors []distribution.Descriptor
// emptyTarDigest is set to a valid digest if an empty tar has been // emptyTarDigest is set to a valid digest if an empty tar has been
@ -54,13 +53,12 @@ type configManifestBuilder struct {
// schema version from an image configuration and a set of descriptors. // schema version from an image configuration and a set of descriptors.
// It takes a BlobService so that it can add an empty tar to the blob store // It takes a BlobService so that it can add an empty tar to the blob store
// if the resulting manifest needs empty layers. // if the resulting manifest needs empty layers.
func NewConfigManifestBuilder(bs distribution.BlobService, pk libtrust.PrivateKey, name, tag string, configJSON []byte) distribution.ManifestBuilder { func NewConfigManifestBuilder(bs distribution.BlobService, pk libtrust.PrivateKey, ref reference.Named, configJSON []byte) distribution.ManifestBuilder {
return &configManifestBuilder{ return &configManifestBuilder{
bs: bs, bs: bs,
pk: pk, pk: pk,
configJSON: configJSON, configJSON: configJSON,
name: name, ref: ref,
tag: tag,
} }
} }
@ -190,12 +188,17 @@ func (mb *configManifestBuilder) Build(ctx context.Context) (m distribution.Mani
history[0].V1Compatibility = string(transformedConfig) history[0].V1Compatibility = string(transformedConfig)
tag := ""
if tagged, isTagged := mb.ref.(reference.Tagged); isTagged {
tag = tagged.Tag()
}
mfst := Manifest{ mfst := Manifest{
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 1,
}, },
Name: mb.name, Name: mb.ref.Name(),
Tag: mb.tag, Tag: tag,
Architecture: img.Architecture, Architecture: img.Architecture,
FSLayers: fsLayerList, FSLayers: fsLayerList,
History: history, History: history,

View file

@ -10,6 +10,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/docker/libtrust" "github.com/docker/libtrust"
) )
@ -194,7 +195,13 @@ func TestConfigBuilder(t *testing.T) {
} }
bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)} bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)}
builder := NewConfigManifestBuilder(bs, pk, "testrepo", "testtag", []byte(imgJSON))
ref, err := reference.ParseNamed("testrepo:testtag")
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
builder := NewConfigManifestBuilder(bs, pk, ref, []byte(imgJSON))
for _, d := range descriptors { for _, d := range descriptors {
if err := builder.AppendReference(d); err != nil { if err := builder.AppendReference(d); err != nil {

View file

@ -8,6 +8,7 @@ import (
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest" "github.com/docker/distribution/manifest"
"github.com/docker/distribution/reference"
"github.com/docker/libtrust" "github.com/docker/libtrust"
) )
@ -20,13 +21,18 @@ type referenceManifestBuilder struct {
// NewReferenceManifestBuilder is used to build new manifests for the current // NewReferenceManifestBuilder is used to build new manifests for the current
// schema version using schema1 dependencies. // schema version using schema1 dependencies.
func NewReferenceManifestBuilder(pk libtrust.PrivateKey, name, tag, architecture string) distribution.ManifestBuilder { func NewReferenceManifestBuilder(pk libtrust.PrivateKey, ref reference.Named, architecture string) distribution.ManifestBuilder {
tag := ""
if tagged, isTagged := ref.(reference.Tagged); isTagged {
tag = tagged.Tag()
}
return &referenceManifestBuilder{ return &referenceManifestBuilder{
Manifest: Manifest{ Manifest: Manifest{
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 1,
}, },
Name: name, Name: ref.Name(),
Tag: tag, Tag: tag,
Architecture: architecture, Architecture: architecture,
}, },

View file

@ -6,6 +6,7 @@ import (
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest" "github.com/docker/distribution/manifest"
"github.com/docker/distribution/reference"
"github.com/docker/libtrust" "github.com/docker/libtrust"
) )
@ -54,7 +55,16 @@ func TestReferenceBuilder(t *testing.T) {
handCrafted := makeSignedManifest(t, pk, []Reference{r1, r2}) handCrafted := makeSignedManifest(t, pk, []Reference{r1, r2})
b := NewReferenceManifestBuilder(pk, handCrafted.Manifest.Name, handCrafted.Manifest.Tag, handCrafted.Manifest.Architecture) ref, err := reference.ParseNamed(handCrafted.Manifest.Name)
if err != nil {
t.Fatalf("could not parse reference: %v", err)
}
ref, err = reference.WithTag(ref, handCrafted.Manifest.Tag)
if err != nil {
t.Fatalf("could not add tag: %v", err)
}
b := NewReferenceManifestBuilder(pk, ref, handCrafted.Manifest.Architecture)
_, err = b.Build(context.Background()) _, err = b.Build(context.Background())
if err == nil { if err == nil {
t.Fatal("Expected error building zero length manifest") t.Fatal("Expected error building zero length manifest")

View file

@ -7,6 +7,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/uuid" "github.com/docker/distribution/uuid"
) )
@ -22,8 +23,8 @@ var _ Listener = &bridge{}
// URLBuilder defines a subset of url builder to be used by the event listener. // URLBuilder defines a subset of url builder to be used by the event listener.
type URLBuilder interface { type URLBuilder interface {
BuildManifestURL(name, tag string) (string, error) BuildManifestURL(name reference.Named, tag string) (string, error)
BuildBlobURL(name string, dgst digest.Digest) (string, error) BuildBlobURL(name reference.Named, dgst digest.Digest) (string, error)
} }
// NewBridge returns a notification listener that writes records to sink, // NewBridge returns a notification listener that writes records to sink,
@ -52,40 +53,40 @@ func NewRequestRecord(id string, r *http.Request) RequestRecord {
} }
} }
func (b *bridge) ManifestPushed(repo string, sm distribution.Manifest) error { func (b *bridge) ManifestPushed(repo reference.Named, sm distribution.Manifest) error {
return b.createManifestEventAndWrite(EventActionPush, repo, sm) return b.createManifestEventAndWrite(EventActionPush, repo, sm)
} }
func (b *bridge) ManifestPulled(repo string, sm distribution.Manifest) error { func (b *bridge) ManifestPulled(repo reference.Named, sm distribution.Manifest) error {
return b.createManifestEventAndWrite(EventActionPull, repo, sm) return b.createManifestEventAndWrite(EventActionPull, repo, sm)
} }
func (b *bridge) ManifestDeleted(repo string, sm distribution.Manifest) error { func (b *bridge) ManifestDeleted(repo reference.Named, sm distribution.Manifest) error {
return b.createManifestEventAndWrite(EventActionDelete, repo, sm) return b.createManifestEventAndWrite(EventActionDelete, repo, sm)
} }
func (b *bridge) BlobPushed(repo string, desc distribution.Descriptor) error { func (b *bridge) BlobPushed(repo reference.Named, desc distribution.Descriptor) error {
return b.createBlobEventAndWrite(EventActionPush, repo, desc) return b.createBlobEventAndWrite(EventActionPush, repo, desc)
} }
func (b *bridge) BlobPulled(repo string, desc distribution.Descriptor) error { func (b *bridge) BlobPulled(repo reference.Named, desc distribution.Descriptor) error {
return b.createBlobEventAndWrite(EventActionPull, repo, desc) return b.createBlobEventAndWrite(EventActionPull, repo, desc)
} }
func (b *bridge) BlobMounted(repo string, desc distribution.Descriptor, fromRepo string) error { func (b *bridge) BlobMounted(repo reference.Named, desc distribution.Descriptor, fromRepo reference.Named) error {
event, err := b.createBlobEvent(EventActionMount, repo, desc) event, err := b.createBlobEvent(EventActionMount, repo, desc)
if err != nil { if err != nil {
return err return err
} }
event.Target.FromRepository = fromRepo event.Target.FromRepository = fromRepo.Name()
return b.sink.Write(*event) return b.sink.Write(*event)
} }
func (b *bridge) BlobDeleted(repo string, desc distribution.Descriptor) error { func (b *bridge) BlobDeleted(repo reference.Named, desc distribution.Descriptor) error {
return b.createBlobEventAndWrite(EventActionDelete, repo, desc) return b.createBlobEventAndWrite(EventActionDelete, repo, desc)
} }
func (b *bridge) createManifestEventAndWrite(action string, repo string, sm distribution.Manifest) error { func (b *bridge) createManifestEventAndWrite(action string, repo reference.Named, sm distribution.Manifest) error {
manifestEvent, err := b.createManifestEvent(action, repo, sm) manifestEvent, err := b.createManifestEvent(action, repo, sm)
if err != nil { if err != nil {
return err return err
@ -94,9 +95,9 @@ func (b *bridge) createManifestEventAndWrite(action string, repo string, sm dist
return b.sink.Write(*manifestEvent) return b.sink.Write(*manifestEvent)
} }
func (b *bridge) createManifestEvent(action string, repo string, sm distribution.Manifest) (*Event, error) { func (b *bridge) createManifestEvent(action string, repo reference.Named, sm distribution.Manifest) (*Event, error) {
event := b.createEvent(action) event := b.createEvent(action)
event.Target.Repository = repo event.Target.Repository = repo.Name()
mt, p, err := sm.Payload() mt, p, err := sm.Payload()
if err != nil { if err != nil {
@ -122,7 +123,7 @@ func (b *bridge) createManifestEvent(action string, repo string, sm distribution
return event, nil return event, nil
} }
func (b *bridge) createBlobEventAndWrite(action string, repo string, desc distribution.Descriptor) error { func (b *bridge) createBlobEventAndWrite(action string, repo reference.Named, desc distribution.Descriptor) error {
event, err := b.createBlobEvent(action, repo, desc) event, err := b.createBlobEvent(action, repo, desc)
if err != nil { if err != nil {
return err return err
@ -131,11 +132,11 @@ func (b *bridge) createBlobEventAndWrite(action string, repo string, desc distri
return b.sink.Write(*event) return b.sink.Write(*event)
} }
func (b *bridge) createBlobEvent(action string, repo string, desc distribution.Descriptor) (*Event, error) { func (b *bridge) createBlobEvent(action string, repo reference.Named, desc distribution.Descriptor) (*Event, error) {
event := b.createEvent(action) event := b.createEvent(action)
event.Target.Descriptor = desc event.Target.Descriptor = desc
event.Target.Length = desc.Size event.Target.Length = desc.Size
event.Target.Repository = repo event.Target.Repository = repo.Name()
var err error var err error
event.Target.URL, err = b.ub.BuildBlobURL(repo, desc.Digest) event.Target.URL, err = b.ub.BuildBlobURL(repo, desc.Digest)

View file

@ -5,6 +5,7 @@ import (
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/api/v2" "github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/uuid" "github.com/docker/distribution/uuid"
"github.com/docker/libtrust" "github.com/docker/libtrust"
@ -35,14 +36,14 @@ var (
) )
func TestEventBridgeManifestPulled(t *testing.T) { func TestEventBridgeManifestPulled(t *testing.T) {
l := createTestEnv(t, testSinkFn(func(events ...Event) error { l := createTestEnv(t, testSinkFn(func(events ...Event) error {
checkCommonManifest(t, EventActionPull, events...) checkCommonManifest(t, EventActionPull, events...)
return nil return nil
})) }))
if err := l.ManifestPulled(repo, sm); err != nil { repoRef, _ := reference.ParseNamed(repo)
if err := l.ManifestPulled(repoRef, sm); err != nil {
t.Fatalf("unexpected error notifying manifest pull: %v", err) t.Fatalf("unexpected error notifying manifest pull: %v", err)
} }
} }
@ -54,7 +55,8 @@ func TestEventBridgeManifestPushed(t *testing.T) {
return nil return nil
})) }))
if err := l.ManifestPushed(repo, sm); err != nil { repoRef, _ := reference.ParseNamed(repo)
if err := l.ManifestPushed(repoRef, sm); err != nil {
t.Fatalf("unexpected error notifying manifest pull: %v", err) t.Fatalf("unexpected error notifying manifest pull: %v", err)
} }
} }
@ -66,7 +68,8 @@ func TestEventBridgeManifestDeleted(t *testing.T) {
return nil return nil
})) }))
if err := l.ManifestDeleted(repo, sm); err != nil { repoRef, _ := reference.ParseNamed(repo)
if err := l.ManifestDeleted(repoRef, sm); err != nil {
t.Fatalf("unexpected error notifying manifest pull: %v", err) t.Fatalf("unexpected error notifying manifest pull: %v", err)
} }
} }
@ -96,7 +99,8 @@ func checkCommonManifest(t *testing.T, action string, events ...Event) {
t.Fatalf("unexpected event action: %q != %q", event.Action, action) t.Fatalf("unexpected event action: %q != %q", event.Action, action)
} }
u, err := ub.BuildManifestURL(repo, dgst.String()) repoRef, _ := reference.ParseNamed(repo)
u, err := ub.BuildManifestURL(repoRef, dgst.String())
if err != nil { if err != nil {
t.Fatalf("error building expected url: %v", err) t.Fatalf("error building expected url: %v", err)
} }

View file

@ -7,29 +7,30 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
) )
// ManifestListener describes a set of methods for listening to events related to manifests. // ManifestListener describes a set of methods for listening to events related to manifests.
type ManifestListener interface { type ManifestListener interface {
ManifestPushed(repo string, sm distribution.Manifest) error ManifestPushed(repo reference.Named, sm distribution.Manifest) error
ManifestPulled(repo string, sm distribution.Manifest) error ManifestPulled(repo reference.Named, sm distribution.Manifest) error
// TODO(stevvooe): Please note that delete support is still a little shaky // TODO(stevvooe): Please note that delete support is still a little shaky
// and we'll need to propagate these in the future. // and we'll need to propagate these in the future.
ManifestDeleted(repo string, sm distribution.Manifest) error ManifestDeleted(repo reference.Named, sm distribution.Manifest) error
} }
// BlobListener describes a listener that can respond to layer related events. // BlobListener describes a listener that can respond to layer related events.
type BlobListener interface { type BlobListener interface {
BlobPushed(repo string, desc distribution.Descriptor) error BlobPushed(repo reference.Named, desc distribution.Descriptor) error
BlobPulled(repo string, desc distribution.Descriptor) error BlobPulled(repo reference.Named, desc distribution.Descriptor) error
BlobMounted(repo string, desc distribution.Descriptor, fromRepo string) error BlobMounted(repo reference.Named, desc distribution.Descriptor, fromRepo reference.Named) error
// TODO(stevvooe): Please note that delete support is still a little shaky // TODO(stevvooe): Please note that delete support is still a little shaky
// and we'll need to propagate these in the future. // and we'll need to propagate these in the future.
BlobDeleted(repo string, desc distribution.Descriptor) error BlobDeleted(repo reference.Named, desc distribution.Descriptor) error
} }
// Listener combines all repository events into a single interface. // Listener combines all repository events into a single interface.
@ -164,7 +165,7 @@ func (bsl *blobServiceListener) Create(ctx context.Context, options ...distribut
wr, err := bsl.BlobStore.Create(ctx, options...) wr, err := bsl.BlobStore.Create(ctx, options...)
switch err := err.(type) { switch err := err.(type) {
case distribution.ErrBlobMounted: case distribution.ErrBlobMounted:
if err := bsl.parent.listener.BlobMounted(bsl.parent.Repository.Name(), err.Descriptor, err.From.Name()); err != nil { if err := bsl.parent.listener.BlobMounted(bsl.parent.Repository.Name(), err.Descriptor, err.From); err != nil {
context.GetLogger(ctx).Errorf("error dispatching blob mount to listener: %v", err) context.GetLogger(ctx).Errorf("error dispatching blob mount to listener: %v", err)
} }
return nil, err return nil, err

View file

@ -10,6 +10,7 @@ import (
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest" "github.com/docker/distribution/manifest"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/storage" "github.com/docker/distribution/registry/storage"
"github.com/docker/distribution/registry/storage/cache/memory" "github.com/docker/distribution/registry/storage/cache/memory"
"github.com/docker/distribution/registry/storage/driver/inmemory" "github.com/docker/distribution/registry/storage/driver/inmemory"
@ -27,7 +28,8 @@ func TestListener(t *testing.T) {
ops: make(map[string]int), ops: make(map[string]int),
} }
repository, err := registry.Repository(ctx, "foo/bar") repoRef, _ := reference.ParseNamed("foo/bar")
repository, err := registry.Repository(ctx, repoRef)
if err != nil { if err != nil {
t.Fatalf("unexpected error getting repo: %v", err) t.Fatalf("unexpected error getting repo: %v", err)
} }
@ -55,38 +57,38 @@ type testListener struct {
ops map[string]int ops map[string]int
} }
func (tl *testListener) ManifestPushed(repo string, m distribution.Manifest) error { func (tl *testListener) ManifestPushed(repo reference.Named, m distribution.Manifest) error {
tl.ops["manifest:push"]++ tl.ops["manifest:push"]++
return nil return nil
} }
func (tl *testListener) ManifestPulled(repo string, m distribution.Manifest) error { func (tl *testListener) ManifestPulled(repo reference.Named, m distribution.Manifest) error {
tl.ops["manifest:pull"]++ tl.ops["manifest:pull"]++
return nil return nil
} }
func (tl *testListener) ManifestDeleted(repo string, m distribution.Manifest) error { func (tl *testListener) ManifestDeleted(repo reference.Named, m distribution.Manifest) error {
tl.ops["manifest:delete"]++ tl.ops["manifest:delete"]++
return nil return nil
} }
func (tl *testListener) BlobPushed(repo string, desc distribution.Descriptor) error { func (tl *testListener) BlobPushed(repo reference.Named, desc distribution.Descriptor) error {
tl.ops["layer:push"]++ tl.ops["layer:push"]++
return nil return nil
} }
func (tl *testListener) BlobPulled(repo string, desc distribution.Descriptor) error { func (tl *testListener) BlobPulled(repo reference.Named, desc distribution.Descriptor) error {
tl.ops["layer:pull"]++ tl.ops["layer:pull"]++
return nil return nil
} }
func (tl *testListener) BlobMounted(repo string, desc distribution.Descriptor, fromRepo string) error { func (tl *testListener) BlobMounted(repo reference.Named, desc distribution.Descriptor, fromRepo reference.Named) error {
tl.ops["layer:mount"]++ tl.ops["layer:mount"]++
return nil return nil
} }
func (tl *testListener) BlobDeleted(repo string, desc distribution.Descriptor) error { func (tl *testListener) BlobDeleted(repo reference.Named, desc distribution.Descriptor) error {
tl.ops["layer:delete"]++ tl.ops["layer:delete"]++
return nil return nil
} }
@ -107,7 +109,7 @@ func checkExerciseRepository(t *testing.T, repository distribution.Repository) {
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 1,
}, },
Name: repository.Name(), Name: repository.Name().Name(),
Tag: tag, Tag: tag,
} }

View file

@ -2,6 +2,7 @@ package distribution
import ( import (
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/reference"
) )
// Scope defines the set of items that match a namespace. // Scope defines the set of items that match a namespace.
@ -32,7 +33,7 @@ type Namespace interface {
// Repository should return a reference to the named repository. The // Repository should return a reference to the named repository. The
// registry may or may not have the repository but should always return a // registry may or may not have the repository but should always return a
// reference. // reference.
Repository(ctx context.Context, name string) (Repository, error) Repository(ctx context.Context, name reference.Named) (Repository, error)
// Repositories fills 'repos' with a lexigraphically sorted catalog of repositories // Repositories fills 'repos' with a lexigraphically sorted catalog of repositories
// up to the size of 'repos' and returns the value 'n' for the number of entries // up to the size of 'repos' and returns the value 'n' for the number of entries
@ -49,7 +50,7 @@ type ManifestServiceOption interface {
// Repository is a named collection of manifests and layers. // Repository is a named collection of manifests and layers.
type Repository interface { type Repository interface {
// Name returns the name of the repository. // Name returns the name of the repository.
Name() string Name() reference.Named
// Manifests returns a reference to this repository's manifest service. // Manifests returns a reference to this repository's manifest service.
// with the supplied options applied. // with the supplied options applied.

View file

@ -6,6 +6,7 @@ import (
"strings" "strings"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -113,10 +114,10 @@ func (ub *URLBuilder) BuildCatalogURL(values ...url.Values) (string, error) {
} }
// BuildTagsURL constructs a url to list the tags in the named repository. // BuildTagsURL constructs a url to list the tags in the named repository.
func (ub *URLBuilder) BuildTagsURL(name string) (string, error) { func (ub *URLBuilder) BuildTagsURL(name reference.Named) (string, error) {
route := ub.cloneRoute(RouteNameTags) route := ub.cloneRoute(RouteNameTags)
tagsURL, err := route.URL("name", name) tagsURL, err := route.URL("name", name.Name())
if err != nil { if err != nil {
return "", err return "", err
} }
@ -126,10 +127,10 @@ func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
// BuildManifestURL constructs a url for the manifest identified by name and // BuildManifestURL constructs a url for the manifest identified by name and
// reference. The argument reference may be either a tag or digest. // reference. The argument reference may be either a tag or digest.
func (ub *URLBuilder) BuildManifestURL(name, reference string) (string, error) { func (ub *URLBuilder) BuildManifestURL(name reference.Named, reference string) (string, error) {
route := ub.cloneRoute(RouteNameManifest) route := ub.cloneRoute(RouteNameManifest)
manifestURL, err := route.URL("name", name, "reference", reference) manifestURL, err := route.URL("name", name.Name(), "reference", reference)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -138,10 +139,10 @@ func (ub *URLBuilder) BuildManifestURL(name, reference string) (string, error) {
} }
// BuildBlobURL constructs the url for the blob identified by name and dgst. // BuildBlobURL constructs the url for the blob identified by name and dgst.
func (ub *URLBuilder) BuildBlobURL(name string, dgst digest.Digest) (string, error) { func (ub *URLBuilder) BuildBlobURL(name reference.Named, dgst digest.Digest) (string, error) {
route := ub.cloneRoute(RouteNameBlob) route := ub.cloneRoute(RouteNameBlob)
layerURL, err := route.URL("name", name, "digest", dgst.String()) layerURL, err := route.URL("name", name.Name(), "digest", dgst.String())
if err != nil { if err != nil {
return "", err return "", err
} }
@ -151,10 +152,10 @@ func (ub *URLBuilder) BuildBlobURL(name string, dgst digest.Digest) (string, err
// BuildBlobUploadURL constructs a url to begin a blob upload in the // BuildBlobUploadURL constructs a url to begin a blob upload in the
// repository identified by name. // repository identified by name.
func (ub *URLBuilder) BuildBlobUploadURL(name string, values ...url.Values) (string, error) { func (ub *URLBuilder) BuildBlobUploadURL(name reference.Named, values ...url.Values) (string, error) {
route := ub.cloneRoute(RouteNameBlobUpload) route := ub.cloneRoute(RouteNameBlobUpload)
uploadURL, err := route.URL("name", name) uploadURL, err := route.URL("name", name.Name())
if err != nil { if err != nil {
return "", err return "", err
} }
@ -166,10 +167,10 @@ func (ub *URLBuilder) BuildBlobUploadURL(name string, values ...url.Values) (str
// including any url values. This should generally not be used by clients, as // including any url values. This should generally not be used by clients, as
// this url is provided by server implementations during the blob upload // this url is provided by server implementations during the blob upload
// process. // process.
func (ub *URLBuilder) BuildBlobUploadChunkURL(name, uuid string, values ...url.Values) (string, error) { func (ub *URLBuilder) BuildBlobUploadChunkURL(name reference.Named, uuid string, values ...url.Values) (string, error) {
route := ub.cloneRoute(RouteNameBlobUploadChunk) route := ub.cloneRoute(RouteNameBlobUploadChunk)
uploadURL, err := route.URL("name", name, "uuid", uuid) uploadURL, err := route.URL("name", name.Name(), "uuid", uuid)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -4,6 +4,8 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"testing" "testing"
"github.com/docker/distribution/reference"
) )
type urlBuilderTestCase struct { type urlBuilderTestCase struct {
@ -13,6 +15,7 @@ type urlBuilderTestCase struct {
} }
func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase { func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
fooBarRef, _ := reference.ParseNamed("foo/bar")
return []urlBuilderTestCase{ return []urlBuilderTestCase{
{ {
description: "test base url", description: "test base url",
@ -23,35 +26,35 @@ func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
description: "test tags url", description: "test tags url",
expectedPath: "/v2/foo/bar/tags/list", expectedPath: "/v2/foo/bar/tags/list",
build: func() (string, error) { build: func() (string, error) {
return urlBuilder.BuildTagsURL("foo/bar") return urlBuilder.BuildTagsURL(fooBarRef)
}, },
}, },
{ {
description: "test manifest url", description: "test manifest url",
expectedPath: "/v2/foo/bar/manifests/tag", expectedPath: "/v2/foo/bar/manifests/tag",
build: func() (string, error) { build: func() (string, error) {
return urlBuilder.BuildManifestURL("foo/bar", "tag") return urlBuilder.BuildManifestURL(fooBarRef, "tag")
}, },
}, },
{ {
description: "build blob url", description: "build blob url",
expectedPath: "/v2/foo/bar/blobs/sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5", expectedPath: "/v2/foo/bar/blobs/sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
build: func() (string, error) { build: func() (string, error) {
return urlBuilder.BuildBlobURL("foo/bar", "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5") return urlBuilder.BuildBlobURL(fooBarRef, "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5")
}, },
}, },
{ {
description: "build blob upload url", description: "build blob upload url",
expectedPath: "/v2/foo/bar/blobs/uploads/", expectedPath: "/v2/foo/bar/blobs/uploads/",
build: func() (string, error) { build: func() (string, error) {
return urlBuilder.BuildBlobUploadURL("foo/bar") return urlBuilder.BuildBlobUploadURL(fooBarRef)
}, },
}, },
{ {
description: "build blob upload url with digest and size", description: "build blob upload url with digest and size",
expectedPath: "/v2/foo/bar/blobs/uploads/?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000", expectedPath: "/v2/foo/bar/blobs/uploads/?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
build: func() (string, error) { build: func() (string, error) {
return urlBuilder.BuildBlobUploadURL("foo/bar", url.Values{ return urlBuilder.BuildBlobUploadURL(fooBarRef, url.Values{
"size": []string{"10000"}, "size": []string{"10000"},
"digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"}, "digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"},
}) })
@ -61,14 +64,14 @@ func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
description: "build blob upload chunk url", description: "build blob upload chunk url",
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part", expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part",
build: func() (string, error) { build: func() (string, error) {
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part") return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part")
}, },
}, },
{ {
description: "build blob upload chunk url with digest and size", description: "build blob upload chunk url with digest and size",
expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000", expectedPath: "/v2/foo/bar/blobs/uploads/uuid-part?digest=sha256%3A3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5&size=10000",
build: func() (string, error) { build: func() (string, error) {
return urlBuilder.BuildBlobUploadChunkURL("foo/bar", "uuid-part", url.Values{ return urlBuilder.BuildBlobUploadChunkURL(fooBarRef, "uuid-part", url.Values{
"size": []string{"10000"}, "size": []string{"10000"},
"digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"}, "digest": []string{"sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"},
}) })

View file

@ -98,11 +98,7 @@ func (r *registry) Repositories(ctx context.Context, entries []string, last stri
} }
// NewRepository creates a new Repository for the given repository name and base URL. // NewRepository creates a new Repository for the given repository name and base URL.
func NewRepository(ctx context.Context, name, baseURL string, transport http.RoundTripper) (distribution.Repository, error) { func NewRepository(ctx context.Context, name reference.Named, baseURL string, transport http.RoundTripper) (distribution.Repository, error) {
if _, err := reference.ParseNamed(name); err != nil {
return nil, err
}
ub, err := v2.NewURLBuilderFromString(baseURL) ub, err := v2.NewURLBuilderFromString(baseURL)
if err != nil { if err != nil {
return nil, err return nil, err
@ -125,21 +121,21 @@ type repository struct {
client *http.Client client *http.Client
ub *v2.URLBuilder ub *v2.URLBuilder
context context.Context context context.Context
name string name reference.Named
} }
func (r *repository) Name() string { func (r *repository) Name() reference.Named {
return r.name return r.name
} }
func (r *repository) Blobs(ctx context.Context) distribution.BlobStore { func (r *repository) Blobs(ctx context.Context) distribution.BlobStore {
statter := &blobStatter{ statter := &blobStatter{
name: r.Name(), name: r.name,
ub: r.ub, ub: r.ub,
client: r.client, client: r.client,
} }
return &blobs{ return &blobs{
name: r.Name(), name: r.name,
ub: r.ub, ub: r.ub,
client: r.client, client: r.client,
statter: cache.NewCachedBlobStatter(memory.NewInMemoryBlobDescriptorCacheProvider(), statter), statter: cache.NewCachedBlobStatter(memory.NewInMemoryBlobDescriptorCacheProvider(), statter),
@ -149,7 +145,7 @@ func (r *repository) Blobs(ctx context.Context) distribution.BlobStore {
func (r *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) { func (r *repository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {
// todo(richardscothern): options should be sent over the wire // todo(richardscothern): options should be sent over the wire
return &manifests{ return &manifests{
name: r.Name(), name: r.name,
ub: r.ub, ub: r.ub,
client: r.client, client: r.client,
etags: make(map[string]string), etags: make(map[string]string),
@ -170,7 +166,7 @@ type tags struct {
client *http.Client client *http.Client
ub *v2.URLBuilder ub *v2.URLBuilder
context context.Context context context.Context
name string name reference.Named
} }
// All returns all tags // All returns all tags
@ -293,7 +289,7 @@ func (t *tags) Untag(ctx context.Context, tag string) error {
} }
type manifests struct { type manifests struct {
name string name reference.Named
ub *v2.URLBuilder ub *v2.URLBuilder
client *http.Client client *http.Client
etags map[string]string etags map[string]string
@ -493,7 +489,7 @@ func (ms *manifests) Delete(ctx context.Context, dgst digest.Digest) error {
}*/ }*/
type blobs struct { type blobs struct {
name string name reference.Named
ub *v2.URLBuilder ub *v2.URLBuilder
client *http.Client client *http.Client
@ -666,7 +662,7 @@ func (bs *blobs) Delete(ctx context.Context, dgst digest.Digest) error {
} }
type blobStatter struct { type blobStatter struct {
name string name reference.Named
ub *v2.URLBuilder ub *v2.URLBuilder
client *http.Client client *http.Client
} }

View file

@ -98,11 +98,11 @@ func addTestCatalog(route string, content []byte, link string, m *testutil.Reque
func TestBlobDelete(t *testing.T) { func TestBlobDelete(t *testing.T) {
dgst, _ := newRandomBlob(1024) dgst, _ := newRandomBlob(1024)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
repo := "test.example.com/repo1" repo, _ := reference.ParseNamed("test.example.com/repo1")
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "DELETE", Method: "DELETE",
Route: "/v2/" + repo + "/blobs/" + dgst.String(), Route: "/v2/" + repo.Name() + "/blobs/" + dgst.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusAccepted, StatusCode: http.StatusAccepted,
@ -137,7 +137,8 @@ func TestBlobFetch(t *testing.T) {
defer c() defer c()
ctx := context.Background() ctx := context.Background()
r, err := NewRepository(ctx, "test.example.com/repo1", e, nil) repo, _ := reference.ParseNamed("test.example.com/repo1")
r, err := NewRepository(ctx, repo, e, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -157,12 +158,12 @@ func TestBlobFetch(t *testing.T) {
func TestBlobExistsNoContentLength(t *testing.T) { func TestBlobExistsNoContentLength(t *testing.T) {
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
repo := "biff" repo, _ := reference.ParseNamed("biff")
dgst, content := newRandomBlob(1024) dgst, content := newRandomBlob(1024)
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + repo + "/blobs/" + dgst.String(), Route: "/v2/" + repo.Name() + "/blobs/" + dgst.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -177,7 +178,7 @@ func TestBlobExistsNoContentLength(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "HEAD", Method: "HEAD",
Route: "/v2/" + repo + "/blobs/" + dgst.String(), Route: "/v2/" + repo.Name() + "/blobs/" + dgst.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -216,7 +217,8 @@ func TestBlobExists(t *testing.T) {
defer c() defer c()
ctx := context.Background() ctx := context.Background()
r, err := NewRepository(ctx, "test.example.com/repo1", e, nil) repo, _ := reference.ParseNamed("test.example.com/repo1")
r, err := NewRepository(ctx, repo, e, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -247,18 +249,18 @@ func TestBlobUploadChunked(t *testing.T) {
b1[512:513], b1[512:513],
b1[513:1024], b1[513:1024],
} }
repo := "test.example.com/uploadrepo" repo, _ := reference.ParseNamed("test.example.com/uploadrepo")
uuids := []string{uuid.Generate().String()} uuids := []string{uuid.Generate().String()}
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "POST", Method: "POST",
Route: "/v2/" + repo + "/blobs/uploads/", Route: "/v2/" + repo.Name() + "/blobs/uploads/",
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusAccepted, StatusCode: http.StatusAccepted,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {"0"}, "Content-Length": {"0"},
"Location": {"/v2/" + repo + "/blobs/uploads/" + uuids[0]}, "Location": {"/v2/" + repo.Name() + "/blobs/uploads/" + uuids[0]},
"Docker-Upload-UUID": {uuids[0]}, "Docker-Upload-UUID": {uuids[0]},
"Range": {"0-0"}, "Range": {"0-0"},
}), }),
@ -271,14 +273,14 @@ func TestBlobUploadChunked(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "PATCH", Method: "PATCH",
Route: "/v2/" + repo + "/blobs/uploads/" + uuids[i], Route: "/v2/" + repo.Name() + "/blobs/uploads/" + uuids[i],
Body: chunk, Body: chunk,
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusAccepted, StatusCode: http.StatusAccepted,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {"0"}, "Content-Length": {"0"},
"Location": {"/v2/" + repo + "/blobs/uploads/" + uuids[i+1]}, "Location": {"/v2/" + repo.Name() + "/blobs/uploads/" + uuids[i+1]},
"Docker-Upload-UUID": {uuids[i+1]}, "Docker-Upload-UUID": {uuids[i+1]},
"Range": {fmt.Sprintf("%d-%d", offset, newOffset-1)}, "Range": {fmt.Sprintf("%d-%d", offset, newOffset-1)},
}), }),
@ -289,7 +291,7 @@ func TestBlobUploadChunked(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "PUT", Method: "PUT",
Route: "/v2/" + repo + "/blobs/uploads/" + uuids[len(uuids)-1], Route: "/v2/" + repo.Name() + "/blobs/uploads/" + uuids[len(uuids)-1],
QueryParams: map[string][]string{ QueryParams: map[string][]string{
"digest": {dgst.String()}, "digest": {dgst.String()},
}, },
@ -306,7 +308,7 @@ func TestBlobUploadChunked(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "HEAD", Method: "HEAD",
Route: "/v2/" + repo + "/blobs/" + dgst.String(), Route: "/v2/" + repo.Name() + "/blobs/" + dgst.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -362,18 +364,18 @@ func TestBlobUploadChunked(t *testing.T) {
func TestBlobUploadMonolithic(t *testing.T) { func TestBlobUploadMonolithic(t *testing.T) {
dgst, b1 := newRandomBlob(1024) dgst, b1 := newRandomBlob(1024)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
repo := "test.example.com/uploadrepo" repo, _ := reference.ParseNamed("test.example.com/uploadrepo")
uploadID := uuid.Generate().String() uploadID := uuid.Generate().String()
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "POST", Method: "POST",
Route: "/v2/" + repo + "/blobs/uploads/", Route: "/v2/" + repo.Name() + "/blobs/uploads/",
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusAccepted, StatusCode: http.StatusAccepted,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {"0"}, "Content-Length": {"0"},
"Location": {"/v2/" + repo + "/blobs/uploads/" + uploadID}, "Location": {"/v2/" + repo.Name() + "/blobs/uploads/" + uploadID},
"Docker-Upload-UUID": {uploadID}, "Docker-Upload-UUID": {uploadID},
"Range": {"0-0"}, "Range": {"0-0"},
}), }),
@ -382,13 +384,13 @@ func TestBlobUploadMonolithic(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "PATCH", Method: "PATCH",
Route: "/v2/" + repo + "/blobs/uploads/" + uploadID, Route: "/v2/" + repo.Name() + "/blobs/uploads/" + uploadID,
Body: b1, Body: b1,
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusAccepted, StatusCode: http.StatusAccepted,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Location": {"/v2/" + repo + "/blobs/uploads/" + uploadID}, "Location": {"/v2/" + repo.Name() + "/blobs/uploads/" + uploadID},
"Docker-Upload-UUID": {uploadID}, "Docker-Upload-UUID": {uploadID},
"Content-Length": {"0"}, "Content-Length": {"0"},
"Docker-Content-Digest": {dgst.String()}, "Docker-Content-Digest": {dgst.String()},
@ -399,7 +401,7 @@ func TestBlobUploadMonolithic(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "PUT", Method: "PUT",
Route: "/v2/" + repo + "/blobs/uploads/" + uploadID, Route: "/v2/" + repo.Name() + "/blobs/uploads/" + uploadID,
QueryParams: map[string][]string{ QueryParams: map[string][]string{
"digest": {dgst.String()}, "digest": {dgst.String()},
}, },
@ -416,7 +418,7 @@ func TestBlobUploadMonolithic(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "HEAD", Method: "HEAD",
Route: "/v2/" + repo + "/blobs/" + dgst.String(), Route: "/v2/" + repo.Name() + "/blobs/" + dgst.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -470,29 +472,22 @@ func TestBlobUploadMonolithic(t *testing.T) {
func TestBlobMount(t *testing.T) { func TestBlobMount(t *testing.T) {
dgst, content := newRandomBlob(1024) dgst, content := newRandomBlob(1024)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
repo := "test.example.com/uploadrepo" repo, _ := reference.ParseNamed("test.example.com/uploadrepo")
sourceRepo := "test.example.com/sourcerepo"
namedRef, err := reference.ParseNamed(sourceRepo) sourceRepo, _ := reference.ParseNamed("test.example.com/sourcerepo")
if err != nil { canonicalRef, _ := reference.WithDigest(sourceRepo, dgst)
t.Fatal(err)
}
canonicalRef, err := reference.WithDigest(namedRef, dgst)
if err != nil {
t.Fatal(err)
}
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "POST", Method: "POST",
Route: "/v2/" + repo + "/blobs/uploads/", Route: "/v2/" + repo.Name() + "/blobs/uploads/",
QueryParams: map[string][]string{"from": {sourceRepo}, "mount": {dgst.String()}}, QueryParams: map[string][]string{"from": {sourceRepo.Name()}, "mount": {dgst.String()}},
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusCreated, StatusCode: http.StatusCreated,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {"0"}, "Content-Length": {"0"},
"Location": {"/v2/" + repo + "/blobs/" + dgst.String()}, "Location": {"/v2/" + repo.Name() + "/blobs/" + dgst.String()},
"Docker-Content-Digest": {dgst.String()}, "Docker-Content-Digest": {dgst.String()},
}), }),
}, },
@ -500,7 +495,7 @@ func TestBlobMount(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "HEAD", Method: "HEAD",
Route: "/v2/" + repo + "/blobs/" + dgst.String(), Route: "/v2/" + repo.Name() + "/blobs/" + dgst.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -531,7 +526,7 @@ func TestBlobMount(t *testing.T) {
if ebm.From.Digest() != dgst { if ebm.From.Digest() != dgst {
t.Fatalf("Unexpected digest: %s, expected %s", ebm.From.Digest(), dgst) t.Fatalf("Unexpected digest: %s, expected %s", ebm.From.Digest(), dgst)
} }
if ebm.From.Name() != sourceRepo { if ebm.From.Name() != sourceRepo.Name() {
t.Fatalf("Unexpected from: %s, expected %s", ebm.From.Name(), sourceRepo) t.Fatalf("Unexpected from: %s, expected %s", ebm.From.Name(), sourceRepo)
} }
} else { } else {
@ -539,7 +534,7 @@ func TestBlobMount(t *testing.T) {
} }
} }
func newRandomSchemaV1Manifest(name, tag string, blobCount int) (*schema1.SignedManifest, digest.Digest, []byte) { func newRandomSchemaV1Manifest(name reference.Named, tag string, blobCount int) (*schema1.SignedManifest, digest.Digest, []byte) {
blobs := make([]schema1.FSLayer, blobCount) blobs := make([]schema1.FSLayer, blobCount)
history := make([]schema1.History, blobCount) history := make([]schema1.History, blobCount)
@ -551,7 +546,7 @@ func newRandomSchemaV1Manifest(name, tag string, blobCount int) (*schema1.Signed
} }
m := schema1.Manifest{ m := schema1.Manifest{
Name: name, Name: name.String(),
Tag: tag, Tag: tag,
Architecture: "x86", Architecture: "x86",
FSLayers: blobs, FSLayers: blobs,
@ -574,11 +569,11 @@ func newRandomSchemaV1Manifest(name, tag string, blobCount int) (*schema1.Signed
return sm, digest.FromBytes(sm.Canonical), sm.Canonical return sm, digest.FromBytes(sm.Canonical), sm.Canonical
} }
func addTestManifestWithEtag(repo, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) { func addTestManifestWithEtag(repo reference.Named, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) {
actualDigest := digest.FromBytes(content) actualDigest := digest.FromBytes(content)
getReqWithEtag := testutil.Request{ getReqWithEtag := testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + repo + "/manifests/" + reference, Route: "/v2/" + repo.Name() + "/manifests/" + reference,
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"If-None-Match": {fmt.Sprintf(`"%s"`, dgst)}, "If-None-Match": {fmt.Sprintf(`"%s"`, dgst)},
}), }),
@ -610,11 +605,11 @@ func addTestManifestWithEtag(repo, reference string, content []byte, m *testutil
*m = append(*m, testutil.RequestResponseMapping{Request: getReqWithEtag, Response: getRespWithEtag}) *m = append(*m, testutil.RequestResponseMapping{Request: getReqWithEtag, Response: getRespWithEtag})
} }
func addTestManifest(repo, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) { func addTestManifest(repo reference.Named, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) {
*m = append(*m, testutil.RequestResponseMapping{ *m = append(*m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + repo + "/manifests/" + reference, Route: "/v2/" + repo.Name() + "/manifests/" + reference,
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -629,7 +624,7 @@ func addTestManifest(repo, reference string, mediatype string, content []byte, m
*m = append(*m, testutil.RequestResponseMapping{ *m = append(*m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "HEAD", Method: "HEAD",
Route: "/v2/" + repo + "/manifests/" + reference, Route: "/v2/" + repo.Name() + "/manifests/" + reference,
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -671,7 +666,7 @@ func checkEqualManifest(m1, m2 *schema1.SignedManifest) error {
func TestV1ManifestFetch(t *testing.T) { func TestV1ManifestFetch(t *testing.T) {
ctx := context.Background() ctx := context.Background()
repo := "test.example.com/repo" repo, _ := reference.ParseNamed("test.example.com/repo")
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
_, pl, err := m1.Payload() _, pl, err := m1.Payload()
@ -743,7 +738,7 @@ func TestV1ManifestFetch(t *testing.T) {
} }
func TestManifestFetchWithEtag(t *testing.T) { func TestManifestFetchWithEtag(t *testing.T) {
repo := "test.example.com/repo/by/tag" repo, _ := reference.ParseNamed("test.example.com/repo/by/tag")
_, d1, p1 := newRandomSchemaV1Manifest(repo, "latest", 6) _, d1, p1 := newRandomSchemaV1Manifest(repo, "latest", 6)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
addTestManifestWithEtag(repo, "latest", p1, &m, d1.String()) addTestManifestWithEtag(repo, "latest", p1, &m, d1.String())
@ -773,14 +768,14 @@ func TestManifestFetchWithEtag(t *testing.T) {
} }
func TestManifestDelete(t *testing.T) { func TestManifestDelete(t *testing.T) {
repo := "test.example.com/repo/delete" repo, _ := reference.ParseNamed("test.example.com/repo/delete")
_, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
_, dgst2, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst2, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "DELETE", Method: "DELETE",
Route: "/v2/" + repo + "/manifests/" + dgst1.String(), Route: "/v2/" + repo.Name() + "/manifests/" + dgst1.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusAccepted, StatusCode: http.StatusAccepted,
@ -813,7 +808,7 @@ func TestManifestDelete(t *testing.T) {
} }
func TestManifestPut(t *testing.T) { func TestManifestPut(t *testing.T) {
repo := "test.example.com/repo/delete" repo, _ := reference.ParseNamed("test.example.com/repo/delete")
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "other", 6) m1, dgst, _ := newRandomSchemaV1Manifest(repo, "other", 6)
_, payload, err := m1.Payload() _, payload, err := m1.Payload()
@ -824,7 +819,7 @@ func TestManifestPut(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "PUT", Method: "PUT",
Route: "/v2/" + repo + "/manifests/other", Route: "/v2/" + repo.Name() + "/manifests/other",
Body: payload, Body: payload,
}, },
Response: testutil.Response{ Response: testutil.Response{
@ -857,7 +852,7 @@ func TestManifestPut(t *testing.T) {
} }
func TestManifestTags(t *testing.T) { func TestManifestTags(t *testing.T) {
repo := "test.example.com/repo/tags/list" repo, _ := reference.ParseNamed("test.example.com/repo/tags/list")
tagsList := []byte(strings.TrimSpace(` tagsList := []byte(strings.TrimSpace(`
{ {
"name": "test.example.com/repo/tags/list", "name": "test.example.com/repo/tags/list",
@ -873,7 +868,7 @@ func TestManifestTags(t *testing.T) {
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + repo + "/tags/list", Route: "/v2/" + repo.Name() + "/tags/list",
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusOK, StatusCode: http.StatusOK,
@ -919,14 +914,14 @@ func TestManifestTags(t *testing.T) {
} }
func TestManifestUnauthorized(t *testing.T) { func TestManifestUnauthorized(t *testing.T) {
repo := "test.example.com/repo" repo, _ := reference.ParseNamed("test.example.com/repo")
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: "GET", Method: "GET",
Route: "/v2/" + repo + "/manifests/" + dgst.String(), Route: "/v2/" + repo.Name() + "/manifests/" + dgst.String(),
}, },
Response: testutil.Response{ Response: testutil.Response{
StatusCode: http.StatusUnauthorized, StatusCode: http.StatusUnauthorized,

View file

@ -26,6 +26,7 @@ import (
"github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/manifest/schema2"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/api/errcode" "github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/api/v2" "github.com/docker/distribution/registry/api/v2"
_ "github.com/docker/distribution/registry/storage/driver/inmemory" _ "github.com/docker/distribution/registry/storage/driver/inmemory"
@ -251,7 +252,7 @@ func TestURLPrefix(t *testing.T) {
} }
type blobArgs struct { type blobArgs struct {
imageName string imageName reference.Named
layerFile io.ReadSeeker layerFile io.ReadSeeker
layerDigest digest.Digest layerDigest digest.Digest
} }
@ -263,10 +264,10 @@ func makeBlobArgs(t *testing.T) blobArgs {
} }
args := blobArgs{ args := blobArgs{
imageName: "foo/bar",
layerFile: layerFile, layerFile: layerFile,
layerDigest: layerDigest, layerDigest: layerDigest,
} }
args.imageName, _ = reference.ParseNamed("foo/bar")
return args return args
} }
@ -609,7 +610,7 @@ func testBlobDelete(t *testing.T, env *testEnv, args blobArgs) {
func TestDeleteDisabled(t *testing.T) { func TestDeleteDisabled(t *testing.T) {
env := newTestEnv(t, false) env := newTestEnv(t, false)
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
// "build" our layer file // "build" our layer file
layerFile, layerDigest, err := testutil.CreateRandomTarFile() layerFile, layerDigest, err := testutil.CreateRandomTarFile()
if err != nil { if err != nil {
@ -634,7 +635,7 @@ func TestDeleteDisabled(t *testing.T) {
func TestDeleteReadOnly(t *testing.T) { func TestDeleteReadOnly(t *testing.T) {
env := newTestEnv(t, true) env := newTestEnv(t, true)
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
// "build" our layer file // "build" our layer file
layerFile, layerDigest, err := testutil.CreateRandomTarFile() layerFile, layerDigest, err := testutil.CreateRandomTarFile()
if err != nil { if err != nil {
@ -662,7 +663,7 @@ func TestStartPushReadOnly(t *testing.T) {
env := newTestEnv(t, true) env := newTestEnv(t, true)
env.app.readOnly = true env.app.readOnly = true
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
layerUploadURL, err := env.builder.BuildBlobUploadURL(imageName) layerUploadURL, err := env.builder.BuildBlobUploadURL(imageName)
if err != nil { if err != nil {
@ -693,42 +694,49 @@ func httpDelete(url string) (*http.Response, error) {
} }
type manifestArgs struct { type manifestArgs struct {
imageName string imageName reference.Named
mediaType string mediaType string
manifest distribution.Manifest manifest distribution.Manifest
dgst digest.Digest dgst digest.Digest
} }
func TestManifestAPI(t *testing.T) { func TestManifestAPI(t *testing.T) {
schema1Repo, _ := reference.ParseNamed("foo/schema1")
schema2Repo, _ := reference.ParseNamed("foo/schema2")
deleteEnabled := false deleteEnabled := false
env := newTestEnv(t, deleteEnabled) env := newTestEnv(t, deleteEnabled)
testManifestAPISchema1(t, env, "foo/schema1") testManifestAPISchema1(t, env, schema1Repo)
schema2Args := testManifestAPISchema2(t, env, "foo/schema2") schema2Args := testManifestAPISchema2(t, env, schema2Repo)
testManifestAPIManifestList(t, env, schema2Args) testManifestAPIManifestList(t, env, schema2Args)
deleteEnabled = true deleteEnabled = true
env = newTestEnv(t, deleteEnabled) env = newTestEnv(t, deleteEnabled)
testManifestAPISchema1(t, env, "foo/schema1") testManifestAPISchema1(t, env, schema1Repo)
schema2Args = testManifestAPISchema2(t, env, "foo/schema2") schema2Args = testManifestAPISchema2(t, env, schema2Repo)
testManifestAPIManifestList(t, env, schema2Args) testManifestAPIManifestList(t, env, schema2Args)
} }
func TestManifestDelete(t *testing.T) { func TestManifestDelete(t *testing.T) {
schema1Repo, _ := reference.ParseNamed("foo/schema1")
schema2Repo, _ := reference.ParseNamed("foo/schema2")
deleteEnabled := true deleteEnabled := true
env := newTestEnv(t, deleteEnabled) env := newTestEnv(t, deleteEnabled)
schema1Args := testManifestAPISchema1(t, env, "foo/schema1") schema1Args := testManifestAPISchema1(t, env, schema1Repo)
testManifestDelete(t, env, schema1Args) testManifestDelete(t, env, schema1Args)
schema2Args := testManifestAPISchema2(t, env, "foo/schema2") schema2Args := testManifestAPISchema2(t, env, schema2Repo)
testManifestDelete(t, env, schema2Args) testManifestDelete(t, env, schema2Args)
} }
func TestManifestDeleteDisabled(t *testing.T) { func TestManifestDeleteDisabled(t *testing.T) {
schema1Repo, _ := reference.ParseNamed("foo/schema1")
deleteEnabled := false deleteEnabled := false
env := newTestEnv(t, deleteEnabled) env := newTestEnv(t, deleteEnabled)
testManifestDeleteDisabled(t, env, "foo/schema1") testManifestDeleteDisabled(t, env, schema1Repo)
} }
func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName string) { func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName reference.Named) {
manifestURL, err := env.builder.BuildManifestURL(imageName, digest.DigestSha256EmptyTar) manifestURL, err := env.builder.BuildManifestURL(imageName, digest.DigestSha256EmptyTar)
if err != nil { if err != nil {
t.Fatalf("unexpected error getting manifest url: %v", err) t.Fatalf("unexpected error getting manifest url: %v", err)
@ -743,7 +751,7 @@ func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName string) {
checkResponse(t, "status of disabled delete of manifest", resp, http.StatusMethodNotAllowed) checkResponse(t, "status of disabled delete of manifest", resp, http.StatusMethodNotAllowed)
} }
func testManifestAPISchema1(t *testing.T, env *testEnv, imageName string) manifestArgs { func testManifestAPISchema1(t *testing.T, env *testEnv, imageName reference.Named) manifestArgs {
tag := "thetag" tag := "thetag"
args := manifestArgs{imageName: imageName} args := manifestArgs{imageName: imageName}
@ -784,7 +792,7 @@ func testManifestAPISchema1(t *testing.T, env *testEnv, imageName string) manife
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 1,
}, },
Name: imageName, Name: imageName.Name(),
Tag: tag, Tag: tag,
FSLayers: []schema1.FSLayer{ FSLayers: []schema1.FSLayer{
{ {
@ -1032,8 +1040,8 @@ func testManifestAPISchema1(t *testing.T, env *testEnv, imageName string) manife
t.Fatalf("unexpected error decoding error response: %v", err) t.Fatalf("unexpected error decoding error response: %v", err)
} }
if tagsResponse.Name != imageName { if tagsResponse.Name != imageName.Name() {
t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName) t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName.Name())
} }
if len(tagsResponse.Tags) != 1 { if len(tagsResponse.Tags) != 1 {
@ -1060,7 +1068,7 @@ func testManifestAPISchema1(t *testing.T, env *testEnv, imageName string) manife
return args return args
} }
func testManifestAPISchema2(t *testing.T, env *testEnv, imageName string) manifestArgs { func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Named) manifestArgs {
tag := "schema2tag" tag := "schema2tag"
args := manifestArgs{ args := manifestArgs{
imageName: imageName, imageName: imageName,
@ -1340,7 +1348,7 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName string) manife
t.Fatalf("unexpected error decoding error response: %v", err) t.Fatalf("unexpected error decoding error response: %v", err)
} }
if tagsResponse.Name != imageName { if tagsResponse.Name != imageName.Name() {
t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName) t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName)
} }
@ -1379,7 +1387,7 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName string) manife
if fetchedSchema1Manifest.Architecture != "amd64" { if fetchedSchema1Manifest.Architecture != "amd64" {
t.Fatal("wrong architecture") t.Fatal("wrong architecture")
} }
if fetchedSchema1Manifest.Name != imageName { if fetchedSchema1Manifest.Name != imageName.Name() {
t.Fatal("wrong image name") t.Fatal("wrong image name")
} }
if fetchedSchema1Manifest.Tag != tag { if fetchedSchema1Manifest.Tag != tag {
@ -1602,7 +1610,7 @@ func testManifestAPIManifestList(t *testing.T, env *testEnv, args manifestArgs)
if fetchedSchema1Manifest.Architecture != "amd64" { if fetchedSchema1Manifest.Architecture != "amd64" {
t.Fatal("wrong architecture") t.Fatal("wrong architecture")
} }
if fetchedSchema1Manifest.Name != imageName { if fetchedSchema1Manifest.Name != imageName.Name() {
t.Fatal("wrong image name") t.Fatal("wrong image name")
} }
if fetchedSchema1Manifest.Tag != tag { if fetchedSchema1Manifest.Tag != tag {
@ -1715,7 +1723,7 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) {
t.Fatalf("unexpected error decoding error response: %v", err) t.Fatalf("unexpected error decoding error response: %v", err)
} }
if tagsResponse.Name != imageName { if tagsResponse.Name != imageName.Name() {
t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName) t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName)
} }
@ -1749,7 +1757,7 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) {
t.Fatalf("unexpected error decoding error response: %v", err) t.Fatalf("unexpected error decoding error response: %v", err)
} }
if tagsResponse.Name != imageName { if tagsResponse.Name != imageName.Name() {
t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName) t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName)
} }
@ -1863,7 +1871,7 @@ func putManifest(t *testing.T, msg, url, contentType string, v interface{}) *htt
return resp return resp
} }
func startPushLayer(t *testing.T, ub *v2.URLBuilder, name string) (location string, uuid string) { func startPushLayer(t *testing.T, ub *v2.URLBuilder, name reference.Named) (location string, uuid string) {
layerUploadURL, err := ub.BuildBlobUploadURL(name) layerUploadURL, err := ub.BuildBlobUploadURL(name)
if err != nil { if err != nil {
t.Fatalf("unexpected error building layer upload url: %v", err) t.Fatalf("unexpected error building layer upload url: %v", err)
@ -1875,7 +1883,7 @@ func startPushLayer(t *testing.T, ub *v2.URLBuilder, name string) (location stri
} }
defer resp.Body.Close() defer resp.Body.Close()
checkResponse(t, fmt.Sprintf("pushing starting layer push %v", name), resp, http.StatusAccepted) checkResponse(t, fmt.Sprintf("pushing starting layer push %v", name.String()), resp, http.StatusAccepted)
u, err := url.Parse(resp.Header.Get("Location")) u, err := url.Parse(resp.Header.Get("Location"))
if err != nil { if err != nil {
@ -1894,7 +1902,7 @@ func startPushLayer(t *testing.T, ub *v2.URLBuilder, name string) (location stri
// doPushLayer pushes the layer content returning the url on success returning // doPushLayer pushes the layer content returning the url on success returning
// the response. If you're only expecting a successful response, use pushLayer. // the response. If you're only expecting a successful response, use pushLayer.
func doPushLayer(t *testing.T, ub *v2.URLBuilder, name string, dgst digest.Digest, uploadURLBase string, body io.Reader) (*http.Response, error) { func doPushLayer(t *testing.T, ub *v2.URLBuilder, name reference.Named, dgst digest.Digest, uploadURLBase string, body io.Reader) (*http.Response, error) {
u, err := url.Parse(uploadURLBase) u, err := url.Parse(uploadURLBase)
if err != nil { if err != nil {
t.Fatalf("unexpected error parsing pushLayer url: %v", err) t.Fatalf("unexpected error parsing pushLayer url: %v", err)
@ -1918,7 +1926,7 @@ func doPushLayer(t *testing.T, ub *v2.URLBuilder, name string, dgst digest.Diges
} }
// pushLayer pushes the layer content returning the url on success. // pushLayer pushes the layer content returning the url on success.
func pushLayer(t *testing.T, ub *v2.URLBuilder, name string, dgst digest.Digest, uploadURLBase string, body io.Reader) string { func pushLayer(t *testing.T, ub *v2.URLBuilder, name reference.Named, dgst digest.Digest, uploadURLBase string, body io.Reader) string {
digester := digest.Canonical.New() digester := digest.Canonical.New()
resp, err := doPushLayer(t, ub, name, dgst, uploadURLBase, io.TeeReader(body, digester.Hash())) resp, err := doPushLayer(t, ub, name, dgst, uploadURLBase, io.TeeReader(body, digester.Hash()))
@ -1949,7 +1957,7 @@ func pushLayer(t *testing.T, ub *v2.URLBuilder, name string, dgst digest.Digest,
return resp.Header.Get("Location") return resp.Header.Get("Location")
} }
func finishUpload(t *testing.T, ub *v2.URLBuilder, name string, uploadURLBase string, dgst digest.Digest) string { func finishUpload(t *testing.T, ub *v2.URLBuilder, name reference.Named, uploadURLBase string, dgst digest.Digest) string {
resp, err := doPushLayer(t, ub, name, dgst, uploadURLBase, nil) resp, err := doPushLayer(t, ub, name, dgst, uploadURLBase, nil)
if err != nil { if err != nil {
t.Fatalf("unexpected error doing push layer request: %v", err) t.Fatalf("unexpected error doing push layer request: %v", err)
@ -1997,7 +2005,7 @@ func doPushChunk(t *testing.T, uploadURLBase string, body io.Reader) (*http.Resp
return resp, digester.Digest(), err return resp, digester.Digest(), err
} }
func pushChunk(t *testing.T, ub *v2.URLBuilder, name string, uploadURLBase string, body io.Reader, length int64) (string, digest.Digest) { func pushChunk(t *testing.T, ub *v2.URLBuilder, name reference.Named, uploadURLBase string, body io.Reader, length int64) (string, digest.Digest) {
resp, dgst, err := doPushChunk(t, uploadURLBase, body) resp, dgst, err := doPushChunk(t, uploadURLBase, body)
if err != nil { if err != nil {
t.Fatalf("unexpected error doing push layer request: %v", err) t.Fatalf("unexpected error doing push layer request: %v", err)
@ -2133,6 +2141,11 @@ func checkErr(t *testing.T, err error, msg string) {
} }
func createRepository(env *testEnv, t *testing.T, imageName string, tag string) digest.Digest { func createRepository(env *testEnv, t *testing.T, imageName string, tag string) digest.Digest {
imageNameRef, err := reference.ParseNamed(imageName)
if err != nil {
t.Fatalf("unable to parse reference: %v", err)
}
unsignedManifest := &schema1.Manifest{ unsignedManifest := &schema1.Manifest{
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 1,
@ -2164,8 +2177,8 @@ func createRepository(env *testEnv, t *testing.T, imageName string, tag string)
expectedLayers[dgst] = rs expectedLayers[dgst] = rs
unsignedManifest.FSLayers[i].BlobSum = dgst unsignedManifest.FSLayers[i].BlobSum = dgst
uploadURLBase, _ := startPushLayer(t, env.builder, imageName) uploadURLBase, _ := startPushLayer(t, env.builder, imageNameRef)
pushLayer(t, env.builder, imageName, dgst, uploadURLBase, rs) pushLayer(t, env.builder, imageNameRef, dgst, uploadURLBase, rs)
} }
signedManifest, err := schema1.Sign(unsignedManifest, env.pk) signedManifest, err := schema1.Sign(unsignedManifest, env.pk)
@ -2176,10 +2189,10 @@ func createRepository(env *testEnv, t *testing.T, imageName string, tag string)
dgst := digest.FromBytes(signedManifest.Canonical) dgst := digest.FromBytes(signedManifest.Canonical)
// Create this repository by tag to ensure the tag mapping is made in the registry // Create this repository by tag to ensure the tag mapping is made in the registry
manifestDigestURL, err := env.builder.BuildManifestURL(imageName, tag) manifestDigestURL, err := env.builder.BuildManifestURL(imageNameRef, tag)
checkErr(t, err, "building manifest url") checkErr(t, err, "building manifest url")
location, err := env.builder.BuildManifestURL(imageName, dgst.String()) location, err := env.builder.BuildManifestURL(imageNameRef, dgst.String())
checkErr(t, err, "building location URL") checkErr(t, err, "building location URL")
resp := putManifest(t, "putting signed manifest", manifestDigestURL, "", signedManifest) resp := putManifest(t, "putting signed manifest", manifestDigestURL, "", signedManifest)
@ -2197,7 +2210,7 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
deleteEnabled := true deleteEnabled := true
env := newTestEnvMirror(t, deleteEnabled) env := newTestEnvMirror(t, deleteEnabled)
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
tag := "latest" tag := "latest"
manifestURL, err := env.builder.BuildManifestURL(imageName, tag) manifestURL, err := env.builder.BuildManifestURL(imageName, tag)
if err != nil { if err != nil {
@ -2209,7 +2222,7 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 1,
}, },
Name: imageName, Name: imageName.Name(),
Tag: tag, Tag: tag,
FSLayers: []schema1.FSLayer{}, FSLayers: []schema1.FSLayer{},
History: []schema1.History{}, History: []schema1.History{},
@ -2284,12 +2297,12 @@ func TestProxyManifestGetByTag(t *testing.T) {
} }
truthConfig.HTTP.Headers = headerConfig truthConfig.HTTP.Headers = headerConfig
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
tag := "latest" tag := "latest"
truthEnv := newTestEnvWithConfig(t, &truthConfig) truthEnv := newTestEnvWithConfig(t, &truthConfig)
// create a repository in the truth registry // create a repository in the truth registry
dgst := createRepository(truthEnv, t, imageName, tag) dgst := createRepository(truthEnv, t, imageName.Name(), tag)
proxyConfig := configuration.Configuration{ proxyConfig := configuration.Configuration{
Storage: configuration.Storage{ Storage: configuration.Storage{
@ -2322,7 +2335,7 @@ func TestProxyManifestGetByTag(t *testing.T) {
}) })
// Create another manifest in the remote with the same image/tag pair // Create another manifest in the remote with the same image/tag pair
newDigest := createRepository(truthEnv, t, imageName, tag) newDigest := createRepository(truthEnv, t, imageName.Name(), tag)
if dgst == newDigest { if dgst == newDigest {
t.Fatalf("non-random test data") t.Fatalf("non-random test data")
} }

View file

@ -18,6 +18,7 @@ import (
"github.com/docker/distribution/health" "github.com/docker/distribution/health"
"github.com/docker/distribution/health/checks" "github.com/docker/distribution/health/checks"
"github.com/docker/distribution/notifications" "github.com/docker/distribution/notifications"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/api/errcode" "github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/api/v2" "github.com/docker/distribution/registry/api/v2"
"github.com/docker/distribution/registry/auth" "github.com/docker/distribution/registry/auth"
@ -590,7 +591,19 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
context.Context = ctxu.WithLogger(context.Context, ctxu.GetLogger(context.Context, "auth.user.name")) context.Context = ctxu.WithLogger(context.Context, ctxu.GetLogger(context.Context, "auth.user.name"))
if app.nameRequired(r) { if app.nameRequired(r) {
repository, err := app.registry.Repository(context, getName(context)) nameRef, err := reference.ParseNamed(getName(context))
if err != nil {
ctxu.GetLogger(context).Errorf("error parsing reference from context: %v", err)
context.Errors = append(context.Errors, distribution.ErrRepositoryNameInvalid{
Name: getName(context),
Reason: err,
})
if err := errcode.ServeJSON(w, context.Errors); err != nil {
ctxu.GetLogger(context).Errorf("error serving error json: %v (from %v)", err, context.Errors)
}
return
}
repository, err := app.registry.Repository(context, nameRef)
if err != nil { if err != nil {
ctxu.GetLogger(context).Errorf("error resolving repository: %v", err) ctxu.GetLogger(context).Errorf("error resolving repository: %v", err)

View file

@ -48,7 +48,7 @@ func TestAppDispatcher(t *testing.T) {
varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc { varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc {
return func(ctx *Context, r *http.Request) http.Handler { return func(ctx *Context, r *http.Request) http.Handler {
// Always checks the same name context // Always checks the same name context
if ctx.Repository.Name() != getName(ctx) { if ctx.Repository.Name().Name() != getName(ctx) {
t.Fatalf("unexpected name: %q != %q", ctx.Repository.Name(), "foo/bar") t.Fatalf("unexpected name: %q != %q", ctx.Repository.Name(), "foo/bar")
} }

View file

@ -46,7 +46,7 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
} }
buh.State = state buh.State = state
if state.Name != ctx.Repository.Name() { if state.Name != ctx.Repository.Name().Name() {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctxu.GetLogger(ctx).Infof("mismatched repository name in upload state: %q != %q", state.Name, buh.Repository.Name()) ctxu.GetLogger(ctx).Infof("mismatched repository name in upload state: %q != %q", state.Name, buh.Repository.Name())
buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadInvalid.WithDetail(err)) buh.Errors = append(buh.Errors, v2.ErrorCodeBlobUploadInvalid.WithDetail(err))
@ -312,7 +312,7 @@ func (buh *blobUploadHandler) blobUploadResponse(w http.ResponseWriter, r *http.
} }
// TODO(stevvooe): Need a better way to manage the upload state automatically. // TODO(stevvooe): Need a better way to manage the upload state automatically.
buh.State.Name = buh.Repository.Name() buh.State.Name = buh.Repository.Name().Name()
buh.State.UUID = buh.Upload.ID() buh.State.UUID = buh.Upload.ID()
buh.State.Offset = offset buh.State.Offset = offset
buh.State.StartedAt = buh.Upload.StartedAt() buh.State.StartedAt = buh.Upload.StartedAt()

View file

@ -11,6 +11,7 @@ import (
"github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/manifest/schema2"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/api/errcode" "github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/api/v2" "github.com/docker/distribution/registry/api/v2"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
@ -173,7 +174,17 @@ func (imh *imageManifestHandler) convertSchema2Manifest(schema2Manifest *schema2
return nil, err return nil, err
} }
builder := schema1.NewConfigManifestBuilder(imh.Repository.Blobs(imh), imh.Context.App.trustKey, imh.Repository.Name(), imh.Tag, configJSON) ref := imh.Repository.Name()
if imh.Tag != "" {
ref, err = reference.WithTag(imh.Repository.Name(), imh.Tag)
if err != nil {
imh.Errors = append(imh.Errors, v2.ErrorCodeTagInvalid.WithDetail(err))
return nil, err
}
}
builder := schema1.NewConfigManifestBuilder(imh.Repository.Blobs(imh), imh.Context.App.trustKey, ref, configJSON)
for _, d := range schema2Manifest.References() { for _, d := range schema2Manifest.References() {
if err := builder.AppendReference(d); err != nil { if err := builder.AppendReference(d); err != nil {
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err)) imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))

View file

@ -40,7 +40,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
switch err := err.(type) { switch err := err.(type) {
case distribution.ErrRepositoryUnknown: case distribution.ErrRepositoryUnknown:
th.Errors = append(th.Errors, v2.ErrorCodeNameUnknown.WithDetail(map[string]string{"name": th.Repository.Name()})) th.Errors = append(th.Errors, v2.ErrorCodeNameUnknown.WithDetail(map[string]string{"name": th.Repository.Name().Name()}))
default: default:
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
} }
@ -51,7 +51,7 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) {
enc := json.NewEncoder(w) enc := json.NewEncoder(w)
if err := enc.Encode(tagsAPIResponse{ if err := enc.Encode(tagsAPIResponse{
Name: th.Repository.Name(), Name: th.Repository.Name().Name(),
Tags: tags, Tags: tags,
}); err != nil { }); err != nil {
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err)) th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))

View file

@ -10,6 +10,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/proxy/scheduler" "github.com/docker/distribution/registry/proxy/scheduler"
) )
@ -133,7 +134,7 @@ func (pbs *proxyBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter,
if err := pbs.storeLocal(ctx, dgst); err != nil { if err := pbs.storeLocal(ctx, dgst); err != nil {
context.GetLogger(ctx).Errorf("Error committing to storage: %s", err.Error()) context.GetLogger(ctx).Errorf("Error committing to storage: %s", err.Error())
} }
pbs.scheduler.AddBlob(dgst.String(), repositoryTTL) pbs.scheduler.AddBlob(dgst, repositoryTTL)
}(dgst) }(dgst)
_, err = pbs.copyContent(ctx, dgst, w) _, err = pbs.copyContent(ctx, dgst, w)
@ -169,7 +170,7 @@ func (pbs *proxyBlobStore) Resume(ctx context.Context, id string) (distribution.
return nil, distribution.ErrUnsupported return nil, distribution.ErrUnsupported
} }
func (pbs *proxyBlobStore) Mount(ctx context.Context, sourceRepo string, dgst digest.Digest) (distribution.Descriptor, error) { func (pbs *proxyBlobStore) Mount(ctx context.Context, sourceRepo reference.Named, dgst digest.Digest) (distribution.Descriptor, error) {
return distribution.Descriptor{}, distribution.ErrUnsupported return distribution.Descriptor{}, distribution.ErrUnsupported
} }

View file

@ -12,6 +12,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/proxy/scheduler" "github.com/docker/distribution/registry/proxy/scheduler"
"github.com/docker/distribution/registry/storage" "github.com/docker/distribution/registry/storage"
"github.com/docker/distribution/registry/storage/cache/memory" "github.com/docker/distribution/registry/storage/cache/memory"
@ -114,6 +115,11 @@ func (te *testEnv) RemoteStats() *map[string]int {
// Populate remote store and record the digests // Populate remote store and record the digests
func makeTestEnv(t *testing.T, name string) *testEnv { func makeTestEnv(t *testing.T, name string) *testEnv {
nameRef, err := reference.ParseNamed(name)
if err != nil {
t.Fatalf("unable to parse reference: %s", err)
}
ctx := context.Background() ctx := context.Background()
truthDir, err := ioutil.TempDir("", "truth") truthDir, err := ioutil.TempDir("", "truth")
@ -131,7 +137,7 @@ func makeTestEnv(t *testing.T, name string) *testEnv {
if err != nil { if err != nil {
t.Fatalf("error creating registry: %v", err) t.Fatalf("error creating registry: %v", err)
} }
localRepo, err := localRegistry.Repository(ctx, name) localRepo, err := localRegistry.Repository(ctx, nameRef)
if err != nil { if err != nil {
t.Fatalf("unexpected error getting repo: %v", err) t.Fatalf("unexpected error getting repo: %v", err)
} }
@ -140,7 +146,7 @@ func makeTestEnv(t *testing.T, name string) *testEnv {
if err != nil { if err != nil {
t.Fatalf("error creating registry: %v", err) t.Fatalf("error creating registry: %v", err)
} }
truthRepo, err := truthRegistry.Repository(ctx, name) truthRepo, err := truthRegistry.Repository(ctx, nameRef)
if err != nil { if err != nil {
t.Fatalf("unexpected error getting repo: %v", err) t.Fatalf("unexpected error getting repo: %v", err)
} }

View file

@ -6,6 +6,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/proxy/scheduler" "github.com/docker/distribution/registry/proxy/scheduler"
) )
@ -16,7 +17,7 @@ type proxyManifestStore struct {
ctx context.Context ctx context.Context
localManifests distribution.ManifestService localManifests distribution.ManifestService
remoteManifests distribution.ManifestService remoteManifests distribution.ManifestService
repositoryName string repositoryName reference.Named
scheduler *scheduler.TTLExpirationScheduler scheduler *scheduler.TTLExpirationScheduler
} }
@ -65,7 +66,7 @@ func (pms proxyManifestStore) Get(ctx context.Context, dgst digest.Digest, optio
pms.scheduler.AddManifest(pms.repositoryName, repositoryTTL) pms.scheduler.AddManifest(pms.repositoryName, repositoryTTL)
// Ensure the manifest blob is cleaned up // Ensure the manifest blob is cleaned up
pms.scheduler.AddBlob(dgst.String(), repositoryTTL) pms.scheduler.AddBlob(dgst, repositoryTTL)
} }
return manifest, err return manifest, err

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest" "github.com/docker/distribution/manifest"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/proxy/scheduler" "github.com/docker/distribution/registry/proxy/scheduler"
"github.com/docker/distribution/registry/storage" "github.com/docker/distribution/registry/storage"
"github.com/docker/distribution/registry/storage/cache/memory" "github.com/docker/distribution/registry/storage/cache/memory"
@ -64,12 +65,17 @@ func (sm statsManifest) Put(ctx context.Context, manifest distribution.Manifest,
*/ */
func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv { func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv {
nameRef, err := reference.ParseNamed(name)
if err != nil {
t.Fatalf("unable to parse reference: %s", err)
}
ctx := context.Background() ctx := context.Background()
truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider())) truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()))
if err != nil { if err != nil {
t.Fatalf("error creating registry: %v", err) t.Fatalf("error creating registry: %v", err)
} }
truthRepo, err := truthRegistry.Repository(ctx, name) truthRepo, err := truthRegistry.Repository(ctx, nameRef)
if err != nil { if err != nil {
t.Fatalf("unexpected error getting repo: %v", err) t.Fatalf("unexpected error getting repo: %v", err)
} }
@ -91,7 +97,7 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE
if err != nil { if err != nil {
t.Fatalf("error creating registry: %v", err) t.Fatalf("error creating registry: %v", err)
} }
localRepo, err := localRegistry.Repository(ctx, name) localRepo, err := localRegistry.Repository(ctx, nameRef)
if err != nil { if err != nil {
t.Fatalf("unexpected error getting repo: %v", err) t.Fatalf("unexpected error getting repo: %v", err)
} }

View file

@ -7,6 +7,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/configuration" "github.com/docker/distribution/configuration"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client" "github.com/docker/distribution/registry/client"
"github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/auth"
"github.com/docker/distribution/registry/client/transport" "github.com/docker/distribution/registry/client/transport"
@ -71,9 +72,9 @@ func (pr *proxyingRegistry) Repositories(ctx context.Context, repos []string, la
return pr.embedded.Repositories(ctx, repos, last) return pr.embedded.Repositories(ctx, repos, last)
} }
func (pr *proxyingRegistry) Repository(ctx context.Context, name string) (distribution.Repository, error) { func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
tr := transport.NewTransport(http.DefaultTransport, tr := transport.NewTransport(http.DefaultTransport,
auth.NewAuthorizer(pr.challengeManager, auth.NewTokenHandler(http.DefaultTransport, pr.credentialStore, name, "pull"))) auth.NewAuthorizer(pr.challengeManager, auth.NewTokenHandler(http.DefaultTransport, pr.credentialStore, name.Name(), "pull")))
localRepo, err := pr.embedded.Repository(ctx, name) localRepo, err := pr.embedded.Repository(ctx, name)
if err != nil { if err != nil {
@ -121,7 +122,7 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name string) (distri
type proxiedRepository struct { type proxiedRepository struct {
blobStore distribution.BlobStore blobStore distribution.BlobStore
manifests distribution.ManifestService manifests distribution.ManifestService
name string name reference.Named
tags distribution.TagService tags distribution.TagService
} }
@ -133,7 +134,7 @@ func (pr *proxiedRepository) Blobs(ctx context.Context) distribution.BlobStore {
return pr.blobStore return pr.blobStore
} }
func (pr *proxiedRepository) Name() string { func (pr *proxiedRepository) Name() reference.Named {
return pr.name return pr.name
} }

View file

@ -7,6 +7,8 @@ import (
"time" "time"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/storage/driver" "github.com/docker/distribution/registry/storage/driver"
) )
@ -80,19 +82,19 @@ func (ttles *TTLExpirationScheduler) OnManifestExpire(f expiryFunc) {
} }
// AddBlob schedules a blob cleanup after ttl expires // AddBlob schedules a blob cleanup after ttl expires
func (ttles *TTLExpirationScheduler) AddBlob(dgst string, ttl time.Duration) error { func (ttles *TTLExpirationScheduler) AddBlob(dgst digest.Digest, ttl time.Duration) error {
ttles.Lock() ttles.Lock()
defer ttles.Unlock() defer ttles.Unlock()
if ttles.stopped { if ttles.stopped {
return fmt.Errorf("scheduler not started") return fmt.Errorf("scheduler not started")
} }
ttles.add(dgst, ttl, entryTypeBlob) ttles.add(dgst.String(), ttl, entryTypeBlob)
return nil return nil
} }
// AddManifest schedules a manifest cleanup after ttl expires // AddManifest schedules a manifest cleanup after ttl expires
func (ttles *TTLExpirationScheduler) AddManifest(repoName string, ttl time.Duration) error { func (ttles *TTLExpirationScheduler) AddManifest(repoName reference.Named, ttl time.Duration) error {
ttles.Lock() ttles.Lock()
defer ttles.Unlock() defer ttles.Unlock()
@ -100,7 +102,7 @@ func (ttles *TTLExpirationScheduler) AddManifest(repoName string, ttl time.Durat
return fmt.Errorf("scheduler not started") return fmt.Errorf("scheduler not started")
} }
ttles.add(repoName, ttl, entryTypeManifest) ttles.add(repoName.Name(), ttl, entryTypeManifest)
return nil return nil
} }

View file

@ -27,7 +27,7 @@ func TestSimpleBlobUpload(t *testing.T) {
} }
ctx := context.Background() ctx := context.Background()
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
driver := inmemory.New() driver := inmemory.New()
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect) registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
if err != nil { if err != nil {
@ -209,7 +209,7 @@ func TestSimpleBlobUpload(t *testing.T) {
// other tests. // other tests.
func TestSimpleBlobRead(t *testing.T) { func TestSimpleBlobRead(t *testing.T) {
ctx := context.Background() ctx := context.Background()
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
driver := inmemory.New() driver := inmemory.New()
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect) registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
if err != nil { if err != nil {
@ -320,8 +320,8 @@ func TestBlobMount(t *testing.T) {
} }
ctx := context.Background() ctx := context.Background()
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
sourceImageName := "foo/source" sourceImageName, _ := reference.ParseNamed("foo/source")
driver := inmemory.New() driver := inmemory.New()
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect) registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
if err != nil { if err != nil {
@ -378,11 +378,7 @@ func TestBlobMount(t *testing.T) {
t.Fatalf("unexpected non-error stating unmounted blob: %v", desc) t.Fatalf("unexpected non-error stating unmounted blob: %v", desc)
} }
namedRef, err := reference.ParseNamed(sourceRepository.Name()) canonicalRef, err := reference.WithDigest(sourceRepository.Name(), desc.Digest)
if err != nil {
t.Fatal(err)
}
canonicalRef, err := reference.WithDigest(namedRef, desc.Digest)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -476,7 +472,7 @@ func TestBlobMount(t *testing.T) {
// TestLayerUploadZeroLength uploads zero-length // TestLayerUploadZeroLength uploads zero-length
func TestLayerUploadZeroLength(t *testing.T) { func TestLayerUploadZeroLength(t *testing.T) {
ctx := context.Background() ctx := context.Background()
imageName := "foo/bar" imageName, _ := reference.ParseNamed("foo/bar")
driver := inmemory.New() driver := inmemory.New()
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect) registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
if err != nil { if err != nil {

View file

@ -326,7 +326,7 @@ func (bw *blobWriter) moveBlob(ctx context.Context, desc distribution.Descriptor
// resources are already not present, no error will be returned. // resources are already not present, no error will be returned.
func (bw *blobWriter) removeResources(ctx context.Context) error { func (bw *blobWriter) removeResources(ctx context.Context) error {
dataPath, err := pathFor(uploadDataPathSpec{ dataPath, err := pathFor(uploadDataPathSpec{
name: bw.blobStore.repository.Name(), name: bw.blobStore.repository.Name().Name(),
id: bw.id, id: bw.id,
}) })

View file

@ -113,7 +113,7 @@ type hashStateEntry struct {
// getStoredHashStates returns a slice of hashStateEntries for this upload. // getStoredHashStates returns a slice of hashStateEntries for this upload.
func (bw *blobWriter) getStoredHashStates(ctx context.Context) ([]hashStateEntry, error) { func (bw *blobWriter) getStoredHashStates(ctx context.Context) ([]hashStateEntry, error) {
uploadHashStatePathPrefix, err := pathFor(uploadHashStatePathSpec{ uploadHashStatePathPrefix, err := pathFor(uploadHashStatePathSpec{
name: bw.blobStore.repository.Name(), name: bw.blobStore.repository.Name().String(),
id: bw.id, id: bw.id,
alg: bw.digester.Digest().Algorithm(), alg: bw.digester.Digest().Algorithm(),
list: true, list: true,
@ -159,7 +159,7 @@ func (bw *blobWriter) storeHashState(ctx context.Context) error {
} }
uploadHashStatePath, err := pathFor(uploadHashStatePathSpec{ uploadHashStatePath, err := pathFor(uploadHashStatePathSpec{
name: bw.blobStore.repository.Name(), name: bw.blobStore.repository.Name().String(),
id: bw.id, id: bw.id,
alg: bw.digester.Digest().Algorithm(), alg: bw.digester.Digest().Algorithm(),
offset: int64(h.Len()), offset: int64(h.Len()),

View file

@ -142,7 +142,7 @@ func (lbs *linkedBlobStore) Create(ctx context.Context, options ...distribution.
} }
if opts.Mount.ShouldMount { if opts.Mount.ShouldMount {
desc, err := lbs.mount(ctx, opts.Mount.From.Name(), opts.Mount.From.Digest()) desc, err := lbs.mount(ctx, opts.Mount.From, opts.Mount.From.Digest())
if err == nil { if err == nil {
// Mount successful, no need to initiate an upload session // Mount successful, no need to initiate an upload session
return nil, distribution.ErrBlobMounted{From: opts.Mount.From, Descriptor: desc} return nil, distribution.ErrBlobMounted{From: opts.Mount.From, Descriptor: desc}
@ -153,7 +153,7 @@ func (lbs *linkedBlobStore) Create(ctx context.Context, options ...distribution.
startedAt := time.Now().UTC() startedAt := time.Now().UTC()
path, err := pathFor(uploadDataPathSpec{ path, err := pathFor(uploadDataPathSpec{
name: lbs.repository.Name(), name: lbs.repository.Name().Name(),
id: uuid, id: uuid,
}) })
@ -162,7 +162,7 @@ func (lbs *linkedBlobStore) Create(ctx context.Context, options ...distribution.
} }
startedAtPath, err := pathFor(uploadStartedAtPathSpec{ startedAtPath, err := pathFor(uploadStartedAtPathSpec{
name: lbs.repository.Name(), name: lbs.repository.Name().Name(),
id: uuid, id: uuid,
}) })
@ -182,7 +182,7 @@ func (lbs *linkedBlobStore) Resume(ctx context.Context, id string) (distribution
context.GetLogger(ctx).Debug("(*linkedBlobStore).Resume") context.GetLogger(ctx).Debug("(*linkedBlobStore).Resume")
startedAtPath, err := pathFor(uploadStartedAtPathSpec{ startedAtPath, err := pathFor(uploadStartedAtPathSpec{
name: lbs.repository.Name(), name: lbs.repository.Name().Name(),
id: id, id: id,
}) })
@ -206,7 +206,7 @@ func (lbs *linkedBlobStore) Resume(ctx context.Context, id string) (distribution
} }
path, err := pathFor(uploadDataPathSpec{ path, err := pathFor(uploadDataPathSpec{
name: lbs.repository.Name(), name: lbs.repository.Name().Name(),
id: id, id: id,
}) })
@ -236,7 +236,7 @@ func (lbs *linkedBlobStore) Delete(ctx context.Context, dgst digest.Digest) erro
return nil return nil
} }
func (lbs *linkedBlobStore) mount(ctx context.Context, sourceRepo string, dgst digest.Digest) (distribution.Descriptor, error) { func (lbs *linkedBlobStore) mount(ctx context.Context, sourceRepo reference.Named, dgst digest.Digest) (distribution.Descriptor, error) {
repo, err := lbs.registry.Repository(ctx, sourceRepo) repo, err := lbs.registry.Repository(ctx, sourceRepo)
if err != nil { if err != nil {
return distribution.Descriptor{}, err return distribution.Descriptor{}, err
@ -298,7 +298,7 @@ func (lbs *linkedBlobStore) linkBlob(ctx context.Context, canonical distribution
} }
seenDigests[dgst] = struct{}{} seenDigests[dgst] = struct{}{}
blobLinkPath, err := linkPathFn(lbs.repository.Name(), dgst) blobLinkPath, err := linkPathFn(lbs.repository.Name().Name(), dgst)
if err != nil { if err != nil {
return err return err
} }
@ -368,7 +368,7 @@ func (lbs *linkedBlobStatter) Stat(ctx context.Context, dgst digest.Digest) (dis
func (lbs *linkedBlobStatter) Clear(ctx context.Context, dgst digest.Digest) (err error) { func (lbs *linkedBlobStatter) Clear(ctx context.Context, dgst digest.Digest) (err error) {
// clear any possible existence of a link described in linkPathFns // clear any possible existence of a link described in linkPathFns
for _, linkPathFn := range lbs.linkPathFns { for _, linkPathFn := range lbs.linkPathFns {
blobLinkPath, err := linkPathFn(lbs.repository.Name(), dgst) blobLinkPath, err := linkPathFn(lbs.repository.Name().Name(), dgst)
if err != nil { if err != nil {
return err return err
} }
@ -391,7 +391,7 @@ func (lbs *linkedBlobStatter) Clear(ctx context.Context, dgst digest.Digest) (er
// linkPathFuncs to let us try a few different paths before returning not // linkPathFuncs to let us try a few different paths before returning not
// found. // found.
func (lbs *linkedBlobStatter) resolveWithLinkFunc(ctx context.Context, dgst digest.Digest, linkPathFn linkPathFunc) (digest.Digest, error) { func (lbs *linkedBlobStatter) resolveWithLinkFunc(ctx context.Context, dgst digest.Digest, linkPathFn linkPathFunc) (digest.Digest, error) {
blobLinkPath, err := linkPathFn(lbs.repository.Name(), dgst) blobLinkPath, err := linkPathFn(lbs.repository.Name().Name(), dgst)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -77,7 +77,7 @@ func (ms *manifestStore) Get(ctx context.Context, dgst digest.Digest, options ..
if err != nil { if err != nil {
if err == distribution.ErrBlobUnknown { if err == distribution.ErrBlobUnknown {
return nil, distribution.ErrManifestUnknownRevision{ return nil, distribution.ErrManifestUnknownRevision{
Name: ms.repository.Name(), Name: ms.repository.Name().Name(),
Revision: dgst, Revision: dgst,
} }
} }

View file

@ -11,6 +11,7 @@ import (
"github.com/docker/distribution/digest" "github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest" "github.com/docker/distribution/manifest"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/storage/cache/memory" "github.com/docker/distribution/registry/storage/cache/memory"
"github.com/docker/distribution/registry/storage/driver" "github.com/docker/distribution/registry/storage/driver"
"github.com/docker/distribution/registry/storage/driver/inmemory" "github.com/docker/distribution/registry/storage/driver/inmemory"
@ -23,11 +24,11 @@ type manifestStoreTestEnv struct {
driver driver.StorageDriver driver driver.StorageDriver
registry distribution.Namespace registry distribution.Namespace
repository distribution.Repository repository distribution.Repository
name string name reference.Named
tag string tag string
} }
func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestEnv { func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string) *manifestStoreTestEnv {
ctx := context.Background() ctx := context.Background()
driver := inmemory.New() driver := inmemory.New()
registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider( registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(
@ -52,7 +53,8 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE
} }
func TestManifestStorage(t *testing.T) { func TestManifestStorage(t *testing.T) {
env := newManifestStoreTestEnv(t, "foo/bar", "thetag") repoName, _ := reference.ParseNamed("foo/bar")
env := newManifestStoreTestEnv(t, repoName, "thetag")
ctx := context.Background() ctx := context.Background()
ms, err := env.repository.Manifests(ctx) ms, err := env.repository.Manifests(ctx)
if err != nil { if err != nil {
@ -63,7 +65,7 @@ func TestManifestStorage(t *testing.T) {
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 1,
}, },
Name: env.name, Name: env.name.Name(),
Tag: env.tag, Tag: env.tag,
} }

View file

@ -107,18 +107,11 @@ func (reg *registry) Scope() distribution.Scope {
// Repository returns an instance of the repository tied to the registry. // Repository returns an instance of the repository tied to the registry.
// Instances should not be shared between goroutines but are cheap to // Instances should not be shared between goroutines but are cheap to
// allocate. In general, they should be request scoped. // allocate. In general, they should be request scoped.
func (reg *registry) Repository(ctx context.Context, canonicalName string) (distribution.Repository, error) { func (reg *registry) Repository(ctx context.Context, canonicalName reference.Named) (distribution.Repository, error) {
if _, err := reference.ParseNamed(canonicalName); err != nil {
return nil, distribution.ErrRepositoryNameInvalid{
Name: canonicalName,
Reason: err,
}
}
var descriptorCache distribution.BlobDescriptorService var descriptorCache distribution.BlobDescriptorService
if reg.blobDescriptorCacheProvider != nil { if reg.blobDescriptorCacheProvider != nil {
var err error var err error
descriptorCache, err = reg.blobDescriptorCacheProvider.RepositoryScoped(canonicalName) descriptorCache, err = reg.blobDescriptorCacheProvider.RepositoryScoped(canonicalName.Name())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -136,12 +129,12 @@ func (reg *registry) Repository(ctx context.Context, canonicalName string) (dist
type repository struct { type repository struct {
*registry *registry
ctx context.Context ctx context.Context
name string name reference.Named
descriptorCache distribution.BlobDescriptorService descriptorCache distribution.BlobDescriptorService
} }
// Name returns the name of the repository. // Name returns the name of the repository.
func (repo *repository) Name() string { func (repo *repository) Name() reference.Named {
return repo.name return repo.name
} }

View file

@ -16,7 +16,7 @@ type signatureStore struct {
func (s *signatureStore) Get(dgst digest.Digest) ([][]byte, error) { func (s *signatureStore) Get(dgst digest.Digest) ([][]byte, error) {
signaturesPath, err := pathFor(manifestSignaturesPathSpec{ signaturesPath, err := pathFor(manifestSignaturesPathSpec{
name: s.repository.Name(), name: s.repository.Name().Name(),
revision: dgst, revision: dgst,
}) })

View file

@ -26,7 +26,7 @@ func (ts *tagStore) All(ctx context.Context) ([]string, error) {
var tags []string var tags []string
pathSpec, err := pathFor(manifestTagPathSpec{ pathSpec, err := pathFor(manifestTagPathSpec{
name: ts.repository.Name(), name: ts.repository.Name().Name(),
}) })
if err != nil { if err != nil {
return tags, err return tags, err
@ -36,7 +36,7 @@ func (ts *tagStore) All(ctx context.Context) ([]string, error) {
if err != nil { if err != nil {
switch err := err.(type) { switch err := err.(type) {
case storagedriver.PathNotFoundError: case storagedriver.PathNotFoundError:
return tags, distribution.ErrRepositoryUnknown{Name: ts.repository.Name()} return tags, distribution.ErrRepositoryUnknown{Name: ts.repository.Name().Name()}
default: default:
return tags, err return tags, err
} }
@ -53,7 +53,7 @@ func (ts *tagStore) All(ctx context.Context) ([]string, error) {
// exists returns true if the specified manifest tag exists in the repository. // exists returns true if the specified manifest tag exists in the repository.
func (ts *tagStore) exists(ctx context.Context, tag string) (bool, error) { func (ts *tagStore) exists(ctx context.Context, tag string) (bool, error) {
tagPath, err := pathFor(manifestTagCurrentPathSpec{ tagPath, err := pathFor(manifestTagCurrentPathSpec{
name: ts.repository.Name(), name: ts.repository.Name().Name(),
tag: tag, tag: tag,
}) })
@ -73,7 +73,7 @@ func (ts *tagStore) exists(ctx context.Context, tag string) (bool, error) {
// the current tag. The digest must point to a manifest. // the current tag. The digest must point to a manifest.
func (ts *tagStore) Tag(ctx context.Context, tag string, desc distribution.Descriptor) error { func (ts *tagStore) Tag(ctx context.Context, tag string, desc distribution.Descriptor) error {
currentPath, err := pathFor(manifestTagCurrentPathSpec{ currentPath, err := pathFor(manifestTagCurrentPathSpec{
name: ts.repository.Name(), name: ts.repository.Name().Name(),
tag: tag, tag: tag,
}) })
@ -95,7 +95,7 @@ func (ts *tagStore) Tag(ctx context.Context, tag string, desc distribution.Descr
// resolve the current revision for name and tag. // resolve the current revision for name and tag.
func (ts *tagStore) Get(ctx context.Context, tag string) (distribution.Descriptor, error) { func (ts *tagStore) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
currentPath, err := pathFor(manifestTagCurrentPathSpec{ currentPath, err := pathFor(manifestTagCurrentPathSpec{
name: ts.repository.Name(), name: ts.repository.Name().Name(),
tag: tag, tag: tag,
}) })
@ -119,7 +119,7 @@ func (ts *tagStore) Get(ctx context.Context, tag string) (distribution.Descripto
// Untag removes the tag association // Untag removes the tag association
func (ts *tagStore) Untag(ctx context.Context, tag string) error { func (ts *tagStore) Untag(ctx context.Context, tag string) error {
tagPath, err := pathFor(manifestTagPathSpec{ tagPath, err := pathFor(manifestTagPathSpec{
name: ts.repository.Name(), name: ts.repository.Name().Name(),
tag: tag, tag: tag,
}) })
@ -172,7 +172,7 @@ func (ts *tagStore) Lookup(ctx context.Context, desc distribution.Descriptor) ([
var tags []string var tags []string
for _, tag := range allTags { for _, tag := range allTags {
tagLinkPathSpec := manifestTagCurrentPathSpec{ tagLinkPathSpec := manifestTagCurrentPathSpec{
name: ts.repository.Name(), name: ts.repository.Name().Name(),
tag: tag, tag: tag,
} }

View file

@ -5,6 +5,7 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/storage/driver/inmemory" "github.com/docker/distribution/registry/storage/driver/inmemory"
) )
@ -21,7 +22,8 @@ func testTagStore(t *testing.T) *tagsTestEnv {
t.Fatal(err) t.Fatal(err)
} }
repo, err := reg.Repository(ctx, "a/b") repoRef, _ := reference.ParseNamed("a/b")
repo, err := reg.Repository(ctx, repoRef)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }