forked from TrueCloudLab/distribution
Merge pull request #1333 from aaronlehmann/use-reference-package
Use reference package
This commit is contained in:
commit
69db5b7440
35 changed files with 462 additions and 297 deletions
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -6,7 +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/reference"
|
||||||
"github.com/docker/distribution/uuid"
|
"github.com/docker/distribution/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,8 +22,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) (string, error)
|
||||||
BuildBlobURL(name string, dgst digest.Digest) (string, error)
|
BuildBlobURL(ref reference.Canonical) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBridge returns a notification listener that writes records to sink,
|
// NewBridge returns a notification listener that writes records to sink,
|
||||||
|
@ -52,40 +52,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 +94,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 {
|
||||||
|
@ -114,7 +114,12 @@ func (b *bridge) createManifestEvent(action string, repo string, sm distribution
|
||||||
event.Target.Size = desc.Size
|
event.Target.Size = desc.Size
|
||||||
event.Target.Digest = desc.Digest
|
event.Target.Digest = desc.Digest
|
||||||
|
|
||||||
event.Target.URL, err = b.ub.BuildManifestURL(repo, event.Target.Digest.String())
|
ref, err := reference.WithDigest(repo, event.Target.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
event.Target.URL, err = b.ub.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -122,7 +127,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,14 +136,18 @@ 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
|
ref, err := reference.WithDigest(repo, desc.Digest)
|
||||||
event.Target.URL, err = b.ub.BuildBlobURL(repo, desc.Digest)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
event.Target.URL, err = b.ub.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,9 @@ 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)
|
||||||
|
ref, _ := reference.WithDigest(repoRef, dgst)
|
||||||
|
u, err := ub.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error building expected url: %v", err)
|
t.Fatalf("error building expected url: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -113,10 +113,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 +126,18 @@ 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(ref reference.Named) (string, error) {
|
||||||
route := ub.cloneRoute(RouteNameManifest)
|
route := ub.cloneRoute(RouteNameManifest)
|
||||||
|
|
||||||
manifestURL, err := route.URL("name", name, "reference", reference)
|
tagOrDigest := ""
|
||||||
|
switch v := ref.(type) {
|
||||||
|
case reference.Tagged:
|
||||||
|
tagOrDigest = v.Tag()
|
||||||
|
case reference.Digested:
|
||||||
|
tagOrDigest = v.Digest().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestURL, err := route.URL("name", ref.Name(), "reference", tagOrDigest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -138,10 +146,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(ref reference.Canonical) (string, error) {
|
||||||
route := ub.cloneRoute(RouteNameBlob)
|
route := ub.cloneRoute(RouteNameBlob)
|
||||||
|
|
||||||
layerURL, err := route.URL("name", name, "digest", dgst.String())
|
layerURL, err := route.URL("name", ref.Name(), "digest", ref.Digest().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -151,10 +159,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 +174,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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,37 @@ 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")
|
ref, _ := reference.WithTag(fooBarRef, "tag")
|
||||||
|
return urlBuilder.BuildManifestURL(ref)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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")
|
ref, _ := reference.WithDigest(fooBarRef, "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5")
|
||||||
|
return urlBuilder.BuildBlobURL(ref)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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 +66,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"},
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
|
@ -253,7 +249,11 @@ func descriptorFromResponse(response *http.Response) (distribution.Descriptor, e
|
||||||
// to construct a descriptor for the tag. If the registry doesn't support HEADing
|
// to construct a descriptor for the tag. If the registry doesn't support HEADing
|
||||||
// a manifest, fallback to GET.
|
// a manifest, fallback to GET.
|
||||||
func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
|
func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, error) {
|
||||||
u, err := t.ub.BuildManifestURL(t.name, tag)
|
ref, err := reference.WithTag(t.name, tag)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
u, err := t.ub.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return distribution.Descriptor{}, err
|
return distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
@ -293,14 +293,18 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *manifests) Exists(ctx context.Context, dgst digest.Digest) (bool, error) {
|
func (ms *manifests) Exists(ctx context.Context, dgst digest.Digest) (bool, error) {
|
||||||
u, err := ms.ub.BuildManifestURL(ms.name, dgst.String())
|
ref, err := reference.WithDigest(ms.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
u, err := ms.ub.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -337,11 +341,19 @@ func (o etagOption) Apply(ms distribution.ManifestService) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
|
func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
|
||||||
|
var (
|
||||||
|
digestOrTag string
|
||||||
|
ref reference.Named
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
var tag string
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
if opt, ok := option.(withTagOption); ok {
|
if opt, ok := option.(withTagOption); ok {
|
||||||
tag = opt.tag
|
digestOrTag = opt.tag
|
||||||
|
ref, err = reference.WithTag(ms.name, opt.tag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err := option.Apply(ms)
|
err := option.Apply(ms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -350,14 +362,15 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ref string
|
if digestOrTag == "" {
|
||||||
if tag != "" {
|
digestOrTag = dgst.String()
|
||||||
ref = tag
|
ref, err = reference.WithDigest(ms.name, dgst)
|
||||||
} else {
|
if err != nil {
|
||||||
ref = dgst.String()
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := ms.ub.BuildManifestURL(ms.name, ref)
|
u, err := ms.ub.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -371,8 +384,8 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
|
||||||
req.Header.Add("Accept", t)
|
req.Header.Add("Accept", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := ms.etags[ref]; ok {
|
if _, ok := ms.etags[digestOrTag]; ok {
|
||||||
req.Header.Set("If-None-Match", ms.etags[ref])
|
req.Header.Set("If-None-Match", ms.etags[digestOrTag])
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := ms.client.Do(req)
|
resp, err := ms.client.Do(req)
|
||||||
|
@ -416,11 +429,15 @@ func (o withTagOption) Apply(m distribution.ManifestService) error {
|
||||||
// Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the
|
// Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the
|
||||||
// tag name in order to build the correct upload URL. This state is written and read under a lock.
|
// tag name in order to build the correct upload URL. This state is written and read under a lock.
|
||||||
func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
|
func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) {
|
||||||
var tag string
|
ref := ms.name
|
||||||
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
if opt, ok := option.(withTagOption); ok {
|
if opt, ok := option.(withTagOption); ok {
|
||||||
tag = opt.tag
|
var err error
|
||||||
|
ref, err = reference.WithTag(ref, opt.tag)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err := option.Apply(ms)
|
err := option.Apply(ms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -429,7 +446,7 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options .
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestURL, err := ms.ub.BuildManifestURL(ms.name, tag)
|
manifestURL, err := ms.ub.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -466,7 +483,11 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options .
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *manifests) Delete(ctx context.Context, dgst digest.Digest) error {
|
func (ms *manifests) Delete(ctx context.Context, dgst digest.Digest) error {
|
||||||
u, err := ms.ub.BuildManifestURL(ms.name, dgst.String())
|
ref, err := reference.WithDigest(ms.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u, err := ms.ub.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -493,7 +514,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
|
||||||
|
|
||||||
|
@ -531,7 +552,11 @@ func (bs *blobs) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *blobs) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
|
func (bs *blobs) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
|
||||||
blobURL, err := bs.ub.BuildBlobURL(bs.name, dgst)
|
ref, err := reference.WithDigest(bs.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
blobURL, err := bs.ub.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -666,13 +691,17 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *blobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
func (bs *blobStatter) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
||||||
u, err := bs.ub.BuildBlobURL(bs.name, dgst)
|
ref, err := reference.WithDigest(bs.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return distribution.Descriptor{}, err
|
||||||
|
}
|
||||||
|
u, err := bs.ub.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return distribution.Descriptor{}, err
|
return distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
@ -720,7 +749,11 @@ func buildCatalogValues(maxEntries int, last string) url.Values {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *blobStatter) Clear(ctx context.Context, dgst digest.Digest) error {
|
func (bs *blobStatter) Clear(ctx context.Context, dgst digest.Digest) error {
|
||||||
blobURL, err := bs.ub.BuildBlobURL(bs.name, dgst)
|
ref, err := reference.WithDigest(bs.name, dgst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
blobURL, err := bs.ub.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +301,8 @@ func TestBlobDeleteDisabled(t *testing.T) {
|
||||||
|
|
||||||
imageName := args.imageName
|
imageName := args.imageName
|
||||||
layerDigest := args.layerDigest
|
layerDigest := args.layerDigest
|
||||||
layerURL, err := env.builder.BuildBlobURL(imageName, layerDigest)
|
ref, _ := reference.WithDigest(imageName, layerDigest)
|
||||||
|
layerURL, err := env.builder.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error building url: %v", err)
|
t.Fatalf("error building url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -323,7 +325,8 @@ func testBlobAPI(t *testing.T, env *testEnv, args blobArgs) *testEnv {
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Test fetch for non-existent content
|
// Test fetch for non-existent content
|
||||||
layerURL, err := env.builder.BuildBlobURL(imageName, layerDigest)
|
ref, _ := reference.WithDigest(imageName, layerDigest)
|
||||||
|
layerURL, err := env.builder.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error building url: %v", err)
|
t.Fatalf("error building url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -533,7 +536,8 @@ func testBlobDelete(t *testing.T, env *testEnv, args blobArgs) {
|
||||||
layerFile := args.layerFile
|
layerFile := args.layerFile
|
||||||
layerDigest := args.layerDigest
|
layerDigest := args.layerDigest
|
||||||
|
|
||||||
layerURL, err := env.builder.BuildBlobURL(imageName, layerDigest)
|
ref, _ := reference.WithDigest(imageName, layerDigest)
|
||||||
|
layerURL, err := env.builder.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -609,14 +613,15 @@ 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 {
|
||||||
t.Fatalf("error creating random layer file: %v", err)
|
t.Fatalf("error creating random layer file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
layerURL, err := env.builder.BuildBlobURL(imageName, layerDigest)
|
ref, _ := reference.WithDigest(imageName, layerDigest)
|
||||||
|
layerURL, err := env.builder.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error building blob URL")
|
t.Fatalf("Error building blob URL")
|
||||||
}
|
}
|
||||||
|
@ -634,14 +639,15 @@ 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 {
|
||||||
t.Fatalf("error creating random layer file: %v", err)
|
t.Fatalf("error creating random layer file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
layerURL, err := env.builder.BuildBlobURL(imageName, layerDigest)
|
ref, _ := reference.WithDigest(imageName, layerDigest)
|
||||||
|
layerURL, err := env.builder.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error building blob URL")
|
t.Fatalf("Error building blob URL")
|
||||||
}
|
}
|
||||||
|
@ -662,7 +668,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,43 +699,51 @@ 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)
|
ref, _ := reference.WithDigest(imageName, digest.DigestSha256EmptyTar)
|
||||||
|
manifestURL, err := env.builder.BuildManifestURL(ref)
|
||||||
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,11 +757,12 @@ 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}
|
||||||
|
|
||||||
manifestURL, err := env.builder.BuildManifestURL(imageName, tag)
|
tagRef, _ := reference.WithTag(imageName, tag)
|
||||||
|
manifestURL, err := env.builder.BuildManifestURL(tagRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error getting manifest url: %v", err)
|
t.Fatalf("unexpected error getting manifest url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -784,7 +799,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{
|
||||||
{
|
{
|
||||||
|
@ -871,7 +886,8 @@ func testManifestAPISchema1(t *testing.T, env *testEnv, imageName string) manife
|
||||||
args.manifest = signedManifest
|
args.manifest = signedManifest
|
||||||
args.dgst = dgst
|
args.dgst = dgst
|
||||||
|
|
||||||
manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String())
|
digestRef, _ := reference.WithDigest(imageName, dgst)
|
||||||
|
manifestDigestURL, err := env.builder.BuildManifestURL(digestRef)
|
||||||
checkErr(t, err, "building manifest url")
|
checkErr(t, err, "building manifest url")
|
||||||
|
|
||||||
resp = putManifest(t, "putting signed manifest no error", manifestURL, "", signedManifest)
|
resp = putManifest(t, "putting signed manifest no error", manifestURL, "", signedManifest)
|
||||||
|
@ -1032,8 +1048,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,14 +1076,15 @@ 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,
|
||||||
mediaType: schema2.MediaTypeManifest,
|
mediaType: schema2.MediaTypeManifest,
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestURL, err := env.builder.BuildManifestURL(imageName, tag)
|
tagRef, _ := reference.WithTag(imageName, tag)
|
||||||
|
manifestURL, err := env.builder.BuildManifestURL(tagRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error getting manifest url: %v", err)
|
t.Fatalf("unexpected error getting manifest url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1211,7 +1228,8 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName string) manife
|
||||||
args.dgst = dgst
|
args.dgst = dgst
|
||||||
args.manifest = deserializedManifest
|
args.manifest = deserializedManifest
|
||||||
|
|
||||||
manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String())
|
digestRef, _ := reference.WithDigest(imageName, dgst)
|
||||||
|
manifestDigestURL, err := env.builder.BuildManifestURL(digestRef)
|
||||||
checkErr(t, err, "building manifest url")
|
checkErr(t, err, "building manifest url")
|
||||||
|
|
||||||
resp = putManifest(t, "putting manifest no error", manifestURL, schema2.MediaTypeManifest, manifest)
|
resp = putManifest(t, "putting manifest no error", manifestURL, schema2.MediaTypeManifest, manifest)
|
||||||
|
@ -1340,7 +1358,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 +1397,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 {
|
||||||
|
@ -1407,7 +1425,8 @@ func testManifestAPIManifestList(t *testing.T, env *testEnv, args manifestArgs)
|
||||||
imageName := args.imageName
|
imageName := args.imageName
|
||||||
tag := "manifestlisttag"
|
tag := "manifestlisttag"
|
||||||
|
|
||||||
manifestURL, err := env.builder.BuildManifestURL(imageName, tag)
|
tagRef, _ := reference.WithTag(imageName, tag)
|
||||||
|
manifestURL, err := env.builder.BuildManifestURL(tagRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error getting manifest url: %v", err)
|
t.Fatalf("unexpected error getting manifest url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1460,7 +1479,8 @@ func testManifestAPIManifestList(t *testing.T, env *testEnv, args manifestArgs)
|
||||||
}
|
}
|
||||||
dgst := digest.FromBytes(canonical)
|
dgst := digest.FromBytes(canonical)
|
||||||
|
|
||||||
manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String())
|
digestRef, _ := reference.WithDigest(imageName, dgst)
|
||||||
|
manifestDigestURL, err := env.builder.BuildManifestURL(digestRef)
|
||||||
checkErr(t, err, "building manifest url")
|
checkErr(t, err, "building manifest url")
|
||||||
|
|
||||||
resp = putManifest(t, "putting manifest list no error", manifestURL, manifestlist.MediaTypeManifestList, deserializedManifestList)
|
resp = putManifest(t, "putting manifest list no error", manifestURL, manifestlist.MediaTypeManifestList, deserializedManifestList)
|
||||||
|
@ -1602,7 +1622,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 {
|
||||||
|
@ -1629,8 +1649,9 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) {
|
||||||
imageName := args.imageName
|
imageName := args.imageName
|
||||||
dgst := args.dgst
|
dgst := args.dgst
|
||||||
manifest := args.manifest
|
manifest := args.manifest
|
||||||
manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String())
|
|
||||||
|
|
||||||
|
ref, _ := reference.WithDigest(imageName, dgst)
|
||||||
|
manifestDigestURL, err := env.builder.BuildManifestURL(ref)
|
||||||
// ---------------
|
// ---------------
|
||||||
// Delete by digest
|
// Delete by digest
|
||||||
resp, err := httpDelete(manifestDigestURL)
|
resp, err := httpDelete(manifestDigestURL)
|
||||||
|
@ -1678,8 +1699,9 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) {
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
// Attempt to delete an unknown manifest
|
// Attempt to delete an unknown manifest
|
||||||
unknownDigest := "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
unknownDigest := digest.Digest("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||||
unknownManifestDigestURL, err := env.builder.BuildManifestURL(imageName, unknownDigest)
|
unknownRef, _ := reference.WithDigest(imageName, unknownDigest)
|
||||||
|
unknownManifestDigestURL, err := env.builder.BuildManifestURL(unknownRef)
|
||||||
checkErr(t, err, "building unknown manifest url")
|
checkErr(t, err, "building unknown manifest url")
|
||||||
|
|
||||||
resp, err = httpDelete(unknownManifestDigestURL)
|
resp, err = httpDelete(unknownManifestDigestURL)
|
||||||
|
@ -1687,11 +1709,12 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) {
|
||||||
checkResponse(t, "fetching deleted manifest", resp, http.StatusNotFound)
|
checkResponse(t, "fetching deleted manifest", resp, http.StatusNotFound)
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
// Uupload manifest by tag
|
// Upload manifest by tag
|
||||||
tag := "atag"
|
tag := "atag"
|
||||||
manifestTagURL, err := env.builder.BuildManifestURL(imageName, tag)
|
tagRef, _ := reference.WithTag(imageName, tag)
|
||||||
resp = putManifest(t, "putting signed manifest by tag", manifestTagURL, args.mediaType, manifest)
|
manifestTagURL, err := env.builder.BuildManifestURL(tagRef)
|
||||||
checkResponse(t, "putting signed manifest by tag", resp, http.StatusCreated)
|
resp = putManifest(t, "putting manifest by tag", manifestTagURL, args.mediaType, manifest)
|
||||||
|
checkResponse(t, "putting manifest by tag", resp, http.StatusCreated)
|
||||||
checkHeaders(t, resp, http.Header{
|
checkHeaders(t, resp, http.Header{
|
||||||
"Location": []string{manifestDigestURL},
|
"Location": []string{manifestDigestURL},
|
||||||
"Docker-Content-Digest": []string{dgst.String()},
|
"Docker-Content-Digest": []string{dgst.String()},
|
||||||
|
@ -1715,7 +1738,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 +1772,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 +1886,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 +1898,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 +1917,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 +1941,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()))
|
||||||
|
@ -1935,7 +1958,8 @@ func pushLayer(t *testing.T, ub *v2.URLBuilder, name string, dgst digest.Digest,
|
||||||
|
|
||||||
sha256Dgst := digester.Digest()
|
sha256Dgst := digester.Digest()
|
||||||
|
|
||||||
expectedLayerURL, err := ub.BuildBlobURL(name, sha256Dgst)
|
ref, _ := reference.WithDigest(name, sha256Dgst)
|
||||||
|
expectedLayerURL, err := ub.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error building expected layer url: %v", err)
|
t.Fatalf("error building expected layer url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1949,7 +1973,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)
|
||||||
|
@ -1958,7 +1982,8 @@ func finishUpload(t *testing.T, ub *v2.URLBuilder, name string, uploadURLBase st
|
||||||
|
|
||||||
checkResponse(t, "putting monolithic chunk", resp, http.StatusCreated)
|
checkResponse(t, "putting monolithic chunk", resp, http.StatusCreated)
|
||||||
|
|
||||||
expectedLayerURL, err := ub.BuildBlobURL(name, dgst)
|
ref, _ := reference.WithDigest(name, dgst)
|
||||||
|
expectedLayerURL, err := ub.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error building expected layer url: %v", err)
|
t.Fatalf("error building expected layer url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -1997,7 +2022,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 +2158,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 +2194,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 +2206,12 @@ 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)
|
tagRef, _ := reference.WithTag(imageNameRef, tag)
|
||||||
|
manifestDigestURL, err := env.builder.BuildManifestURL(tagRef)
|
||||||
checkErr(t, err, "building manifest url")
|
checkErr(t, err, "building manifest url")
|
||||||
|
|
||||||
location, err := env.builder.BuildManifestURL(imageName, dgst.String())
|
digestRef, _ := reference.WithDigest(imageNameRef, dgst)
|
||||||
|
location, err := env.builder.BuildManifestURL(digestRef)
|
||||||
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,9 +2229,10 @@ 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)
|
tagRef, _ := reference.WithTag(imageName, tag)
|
||||||
|
manifestURL, err := env.builder.BuildManifestURL(tagRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error building base url: %v", err)
|
t.Fatalf("unexpected error building base url: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2209,7 +2242,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{},
|
||||||
|
@ -2242,7 +2275,8 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
|
||||||
checkResponse(t, fmt.Sprintf("starting layer push to cache %v", imageName), resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
checkResponse(t, fmt.Sprintf("starting layer push to cache %v", imageName), resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
||||||
|
|
||||||
// Blob Delete
|
// Blob Delete
|
||||||
blobURL, err := env.builder.BuildBlobURL(imageName, digest.DigestSha256EmptyTar)
|
ref, _ := reference.WithDigest(imageName, digest.DigestSha256EmptyTar)
|
||||||
|
blobURL, err := env.builder.BuildBlobURL(ref)
|
||||||
resp, err = httpDelete(blobURL)
|
resp, err = httpDelete(blobURL)
|
||||||
checkResponse(t, "deleting blob from cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
checkResponse(t, "deleting blob from cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
||||||
|
|
||||||
|
@ -2284,12 +2318,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{
|
||||||
|
@ -2303,14 +2337,16 @@ func TestProxyManifestGetByTag(t *testing.T) {
|
||||||
|
|
||||||
proxyEnv := newTestEnvWithConfig(t, &proxyConfig)
|
proxyEnv := newTestEnvWithConfig(t, &proxyConfig)
|
||||||
|
|
||||||
manifestDigestURL, err := proxyEnv.builder.BuildManifestURL(imageName, dgst.String())
|
digestRef, _ := reference.WithDigest(imageName, dgst)
|
||||||
|
manifestDigestURL, err := proxyEnv.builder.BuildManifestURL(digestRef)
|
||||||
checkErr(t, err, "building manifest url")
|
checkErr(t, err, "building manifest url")
|
||||||
|
|
||||||
resp, err := http.Get(manifestDigestURL)
|
resp, err := http.Get(manifestDigestURL)
|
||||||
checkErr(t, err, "fetching manifest from proxy by digest")
|
checkErr(t, err, "fetching manifest from proxy by digest")
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
manifestTagURL, err := proxyEnv.builder.BuildManifestURL(imageName, tag)
|
tagRef, _ := reference.WithTag(imageName, tag)
|
||||||
|
manifestTagURL, err := proxyEnv.builder.BuildManifestURL(tagRef)
|
||||||
checkErr(t, err, "building manifest url")
|
checkErr(t, err, "building manifest url")
|
||||||
|
|
||||||
resp, err = http.Get(manifestTagURL)
|
resp, err = http.Get(manifestTagURL)
|
||||||
|
@ -2322,7 +2358,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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
@ -372,7 +372,11 @@ func (buh *blobUploadHandler) createBlobMountOption(fromRepo, mountDigest string
|
||||||
// created blob. A 201 Created is written as well as the canonical URL and
|
// created blob. A 201 Created is written as well as the canonical URL and
|
||||||
// blob digest.
|
// blob digest.
|
||||||
func (buh *blobUploadHandler) writeBlobCreatedHeaders(w http.ResponseWriter, desc distribution.Descriptor) error {
|
func (buh *blobUploadHandler) writeBlobCreatedHeaders(w http.ResponseWriter, desc distribution.Descriptor) error {
|
||||||
blobURL, err := buh.urlBuilder.BuildBlobURL(buh.Repository.Name(), desc.Digest)
|
ref, err := reference.WithDigest(buh.Repository.Name(), desc.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
blobURL, err := buh.urlBuilder.BuildBlobURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
@ -278,7 +289,13 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a canonical url for the uploaded manifest.
|
// Construct a canonical url for the uploaded manifest.
|
||||||
location, err := imh.urlBuilder.BuildManifestURL(imh.Repository.Name(), imh.Digest.String())
|
ref, err := reference.WithDigest(imh.Repository.Name(), imh.Digest)
|
||||||
|
if err != nil {
|
||||||
|
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
location, err := imh.urlBuilder.BuildManifestURL(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// NOTE(stevvooe): Given the behavior above, this absurdly unlikely to
|
// NOTE(stevvooe): Given the behavior above, this absurdly unlikely to
|
||||||
// happen. We'll log the error here but proceed as if it worked. Worst
|
// happen. We'll log the error here but proceed as if it worked. Worst
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue