Merge pull request #1522 from RichardScothern/tag-events
Send tag events to notification listeners
This commit is contained in:
commit
87a997249d
10 changed files with 155 additions and 71 deletions
|
@ -65,45 +65,64 @@ Events have a well-defined JSON structure and are sent as the body of
|
||||||
notification requests. One or more events are sent in a structure called an
|
notification requests. One or more events are sent in a structure called an
|
||||||
envelope. Each event has a unique id that can be used to uniquely identify incoming
|
envelope. Each event has a unique id that can be used to uniquely identify incoming
|
||||||
requests, if required. Along with that, an _action_ is provided with a
|
requests, if required. Along with that, an _action_ is provided with a
|
||||||
_target, identifying the object mutated during the event.
|
_target_, identifying the object mutated during the event.
|
||||||
|
|
||||||
|
The fields available in an `event` are described below.
|
||||||
|
|
||||||
|
Field | Type | Description
|
||||||
|
----- | ----- | -------------
|
||||||
|
id | string |ID provides a unique identifier for the event.
|
||||||
|
timestamp | Time | Timestamp is the time at which the event occurred.
|
||||||
|
action | string | Action indicates what action encompasses the provided event.
|
||||||
|
target | distribution.Descriptor | Target uniquely describes the target of the event.
|
||||||
|
length | int | Length in bytes of content. Same as Size field in Descriptor.
|
||||||
|
repository | string | Repository identifies the named repository.
|
||||||
|
fromRepository | string | FromRepository identifies the named repository which a blob was mounted from if appropriate.
|
||||||
|
url | string | URL provides a direct link to the content.
|
||||||
|
tag | string | Tag identifies a tag name in tag events
|
||||||
|
request | [RequestRecord](https://godoc.org/github.com/docker/distribution/notifications#RequestRecord) | Request covers the request that generated the event.
|
||||||
|
actor | [ActorRecord](https://godoc.org/github.com/docker/distribution/notifications#ActorRecord). | Actor specifies the agent that initiated the event. For most situations, this could be from the authorization context of the request.
|
||||||
|
source | [SourceRecord](https://godoc.org/github.com/docker/distribution/notifications#SourceRecord) | Source identifies the registry node that generated the event. Put differently, while the actor "initiates" the event, the source "generates" it.
|
||||||
|
|
||||||
The fields available in an event are described in detail in the
|
|
||||||
[godoc](http://godoc.org/github.com/docker/distribution/notifications#Event).
|
|
||||||
|
|
||||||
**TODO:** Let's break out the fields here rather than rely on the godoc.
|
|
||||||
|
|
||||||
The following is an example of a JSON event, sent in response to the push of a
|
The following is an example of a JSON event, sent in response to the push of a
|
||||||
manifest:
|
manifest:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "asdf-asdf-asdf-asdf-0",
|
"events": [
|
||||||
"timestamp": "2006-01-02T15:04:05Z",
|
{
|
||||||
"action": "push",
|
"id": "320678d8-ca14-430f-8bb6-4ca139cd83f7",
|
||||||
"target": {
|
"timestamp": "2016-03-09T14:44:26.402973972-08:00",
|
||||||
"mediaType": "application/vnd.docker.distribution.manifest.v1+json",
|
"action": "pull",
|
||||||
"size": 1,
|
"target": {
|
||||||
"digest": "sha256:0123456789abcdef0",
|
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||||
"length": 1,
|
"size": 708,
|
||||||
"repository": "library/test",
|
"digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
|
||||||
"url": "http://example.com/v2/library/test/manifests/latest"
|
"length": 708,
|
||||||
},
|
"repository": "hello-world",
|
||||||
"request": {
|
"url": "http://192.168.100.227:5000/v2/hello-world/manifests/sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
|
||||||
"id": "asdfasdf",
|
"tag": "latest"
|
||||||
"addr": "client.local",
|
},
|
||||||
"host": "registrycluster.local",
|
"request": {
|
||||||
"method": "PUT",
|
"id": "6df24a34-0959-4923-81ca-14f09767db19",
|
||||||
"useragent": "test/0.1"
|
"addr": "192.168.64.11:42961",
|
||||||
},
|
"host": "192.168.100.227:5000",
|
||||||
"actor": {
|
"method": "GET",
|
||||||
"name": "test-actor"
|
"useragent": "curl/7.38.0"
|
||||||
},
|
},
|
||||||
"source": {
|
"actor": {},
|
||||||
"addr": "hostname.local:port"
|
"source": {
|
||||||
}
|
"addr": "xtal.local:5000",
|
||||||
|
"instanceID": "a53db899-3b4b-4a62-a067-8dd013beaca4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The target struct of events which are sent when manifests and blobs are deleted
|
The target struct of events which are sent when manifests and blobs are deleted
|
||||||
will contain a subset of the data contained in Get and Put events. Specifically,
|
will contain a subset of the data contained in Get and Put events. Specifically,
|
||||||
only the digest and repository will be sent.
|
only the digest and repository will be sent.
|
||||||
|
@ -156,9 +175,9 @@ Content-Type: application/vnd.docker.distribution.events.v1+json
|
||||||
"target": {
|
"target": {
|
||||||
"mediaType": "application/vnd.docker.distribution.manifest.v1+json",
|
"mediaType": "application/vnd.docker.distribution.manifest.v1+json",
|
||||||
"length": 1,
|
"length": 1,
|
||||||
"digest": "sha256:0123456789abcdef0",
|
"digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
|
||||||
"repository": "library/test",
|
"repository": "library/test",
|
||||||
"url": "http://example.com/v2/library/test/manifests/latest"
|
"url": "http://example.com/v2/library/test/manifests/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
"id": "asdfasdf",
|
"id": "asdfasdf",
|
||||||
|
@ -181,9 +200,9 @@ Content-Type: application/vnd.docker.distribution.events.v1+json
|
||||||
"target": {
|
"target": {
|
||||||
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
||||||
"length": 2,
|
"length": 2,
|
||||||
"digest": "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
|
"digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
|
||||||
"repository": "library/test",
|
"repository": "library/test",
|
||||||
"url": "http://example.com/v2/library/test/manifests/latest"
|
"url": "http://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
"id": "asdfasdf",
|
"id": "asdfasdf",
|
||||||
|
@ -206,9 +225,9 @@ Content-Type: application/vnd.docker.distribution.events.v1+json
|
||||||
"target": {
|
"target": {
|
||||||
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
||||||
"length": 3,
|
"length": 3,
|
||||||
"digest": "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d6",
|
"digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
|
||||||
"repository": "library/test",
|
"repository": "library/test",
|
||||||
"url": "http://example.com/v2/library/test/manifests/latest"
|
"url": "http://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
"id": "asdfasdf",
|
"id": "asdfasdf",
|
||||||
|
|
|
@ -53,12 +53,34 @@ func NewRequestRecord(id string, r *http.Request) RequestRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) ManifestPushed(repo reference.Named, sm distribution.Manifest) error {
|
func (b *bridge) ManifestPushed(repo reference.Named, sm distribution.Manifest, options ...distribution.ManifestServiceOption) error {
|
||||||
return b.createManifestEventAndWrite(EventActionPush, repo, sm)
|
manifestEvent, err := b.createManifestEvent(EventActionPush, repo, sm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
if opt, ok := option.(distribution.WithTagOption); ok {
|
||||||
|
manifestEvent.Target.Tag = opt.Tag
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.sink.Write(*manifestEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) ManifestPulled(repo reference.Named, sm distribution.Manifest) error {
|
func (b *bridge) ManifestPulled(repo reference.Named, sm distribution.Manifest, options ...distribution.ManifestServiceOption) error {
|
||||||
return b.createManifestEventAndWrite(EventActionPull, repo, sm)
|
manifestEvent, err := b.createManifestEvent(EventActionPull, repo, sm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
if opt, ok := option.(distribution.WithTagOption); ok {
|
||||||
|
manifestEvent.Target.Tag = opt.Tag
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.sink.Write(*manifestEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) ManifestDeleted(repo reference.Named, dgst digest.Digest) error {
|
func (b *bridge) ManifestDeleted(repo reference.Named, dgst digest.Digest) error {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package notifications
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/distribution"
|
||||||
"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/reference"
|
||||||
|
@ -61,6 +62,38 @@ func TestEventBridgeManifestPushed(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEventBridgeManifestPushedWithTag(t *testing.T) {
|
||||||
|
l := createTestEnv(t, testSinkFn(func(events ...Event) error {
|
||||||
|
checkCommonManifest(t, EventActionPush, events...)
|
||||||
|
if events[0].Target.Tag != "latest" {
|
||||||
|
t.Fatalf("missing or unexpected tag: %#v", events[0].Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
|
||||||
|
repoRef, _ := reference.ParseNamed(repo)
|
||||||
|
if err := l.ManifestPushed(repoRef, sm, distribution.WithTag(m.Tag)); err != nil {
|
||||||
|
t.Fatalf("unexpected error notifying manifest pull: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEventBridgeManifestPulledWithTag(t *testing.T) {
|
||||||
|
l := createTestEnv(t, testSinkFn(func(events ...Event) error {
|
||||||
|
checkCommonManifest(t, EventActionPull, events...)
|
||||||
|
if events[0].Target.Tag != "latest" {
|
||||||
|
t.Fatalf("missing or unexpected tag: %#v", events[0].Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
|
||||||
|
repoRef, _ := reference.ParseNamed(repo)
|
||||||
|
if err := l.ManifestPulled(repoRef, sm, distribution.WithTag(m.Tag)); err != nil {
|
||||||
|
t.Fatalf("unexpected error notifying manifest pull: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEventBridgeManifestDeleted(t *testing.T) {
|
func TestEventBridgeManifestDeleted(t *testing.T) {
|
||||||
l := createTestEnv(t, testSinkFn(func(events ...Event) error {
|
l := createTestEnv(t, testSinkFn(func(events ...Event) error {
|
||||||
checkDeleted(t, EventActionDelete, events...)
|
checkDeleted(t, EventActionDelete, events...)
|
||||||
|
|
|
@ -68,6 +68,9 @@ type Event struct {
|
||||||
|
|
||||||
// URL provides a direct link to the content.
|
// URL provides a direct link to the content.
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
|
|
||||||
|
// Tag provides the tag
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
} `json:"target,omitempty"`
|
} `json:"target,omitempty"`
|
||||||
|
|
||||||
// Request covers the request that generated the event.
|
// Request covers the request that generated the event.
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
|
|
||||||
// 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 reference.Named, sm distribution.Manifest) error
|
ManifestPushed(repo reference.Named, sm distribution.Manifest, options ...distribution.ManifestServiceOption) error
|
||||||
ManifestPulled(repo reference.Named, sm distribution.Manifest) error
|
ManifestPulled(repo reference.Named, sm distribution.Manifest, options ...distribution.ManifestServiceOption) error
|
||||||
ManifestDeleted(repo reference.Named, dgst digest.Digest) error
|
ManifestDeleted(repo reference.Named, dgst digest.Digest) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func (msl *manifestServiceListener) Delete(ctx context.Context, dgst digest.Dige
|
||||||
func (msl *manifestServiceListener) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
|
func (msl *manifestServiceListener) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
|
||||||
sm, err := msl.ManifestService.Get(ctx, dgst)
|
sm, err := msl.ManifestService.Get(ctx, dgst)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if err := msl.parent.listener.ManifestPulled(msl.parent.Repository.Named(), sm); err != nil {
|
if err := msl.parent.listener.ManifestPulled(msl.parent.Repository.Named(), sm, options...); err != nil {
|
||||||
logrus.Errorf("error dispatching manifest pull to listener: %v", err)
|
logrus.Errorf("error dispatching manifest pull to listener: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func (msl *manifestServiceListener) Put(ctx context.Context, sm distribution.Man
|
||||||
dgst, err := msl.ManifestService.Put(ctx, sm, options...)
|
dgst, err := msl.ManifestService.Put(ctx, sm, options...)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if err := msl.parent.listener.ManifestPushed(msl.parent.Repository.Named(), sm); err != nil {
|
if err := msl.parent.listener.ManifestPushed(msl.parent.Repository.Named(), sm, options...); err != nil {
|
||||||
logrus.Errorf("error dispatching manifest push to listener: %v", err)
|
logrus.Errorf("error dispatching manifest push to listener: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ func TestListener(t *testing.T) {
|
||||||
"manifest:delete": 1,
|
"manifest:delete": 1,
|
||||||
"layer:push": 2,
|
"layer:push": 2,
|
||||||
"layer:pull": 2,
|
"layer:pull": 2,
|
||||||
"layer:delete": 2, // deletes not supported for now
|
"layer:delete": 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(tl.ops, expectedOps) {
|
if !reflect.DeepEqual(tl.ops, expectedOps) {
|
||||||
|
@ -57,13 +57,13 @@ type testListener struct {
|
||||||
ops map[string]int
|
ops map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tl *testListener) ManifestPushed(repo reference.Named, m distribution.Manifest) error {
|
func (tl *testListener) ManifestPushed(repo reference.Named, m distribution.Manifest, options ...distribution.ManifestServiceOption) error {
|
||||||
tl.ops["manifest:push"]++
|
tl.ops["manifest:push"]++
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tl *testListener) ManifestPulled(repo reference.Named, m distribution.Manifest) error {
|
func (tl *testListener) ManifestPulled(repo reference.Named, m distribution.Manifest, options ...distribution.ManifestServiceOption) error {
|
||||||
tl.ops["manifest:pull"]++
|
tl.ops["manifest:pull"]++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
14
registry.go
14
registry.go
|
@ -58,6 +58,20 @@ type ManifestServiceOption interface {
|
||||||
Apply(ManifestService) error
|
Apply(ManifestService) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTag allows a tag to be passed into Put
|
||||||
|
func WithTag(tag string) ManifestServiceOption {
|
||||||
|
return WithTagOption{tag}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTagOption holds a tag
|
||||||
|
type WithTagOption struct{ Tag string }
|
||||||
|
|
||||||
|
// Apply conforms to the ManifestServiceOption interface
|
||||||
|
func (o WithTagOption) Apply(m ManifestService) error {
|
||||||
|
// no implementation
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Repository is a named collection of manifests and layers.
|
// Repository is a named collection of manifests and layers.
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
// Named returns the name of the repository.
|
// Named returns the name of the repository.
|
||||||
|
|
|
@ -402,9 +402,9 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
if opt, ok := option.(withTagOption); ok {
|
if opt, ok := option.(distribution.WithTagOption); ok {
|
||||||
digestOrTag = opt.tag
|
digestOrTag = opt.Tag
|
||||||
ref, err = reference.WithTag(ms.name, opt.tag)
|
ref, err = reference.WithTag(ms.name, opt.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -465,21 +465,6 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis
|
||||||
return nil, HandleErrorResponse(resp)
|
return nil, HandleErrorResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTag allows a tag to be passed into Put which enables the client
|
|
||||||
// to build a correct URL.
|
|
||||||
func WithTag(tag string) distribution.ManifestServiceOption {
|
|
||||||
return withTagOption{tag}
|
|
||||||
}
|
|
||||||
|
|
||||||
type withTagOption struct{ tag string }
|
|
||||||
|
|
||||||
func (o withTagOption) Apply(m distribution.ManifestService) error {
|
|
||||||
if _, ok := m.(*manifests); ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("withTagOption is a client-only option")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
// tag name in order to build the correct upload URL.
|
||||||
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) {
|
||||||
|
@ -487,9 +472,9 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options .
|
||||||
var tagged bool
|
var tagged bool
|
||||||
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
if opt, ok := option.(withTagOption); ok {
|
if opt, ok := option.(distribution.WithTagOption); ok {
|
||||||
var err error
|
var err error
|
||||||
ref, err = reference.WithTag(ref, opt.tag)
|
ref, err = reference.WithTag(ref, opt.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -710,7 +710,7 @@ func TestV1ManifestFetch(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err = ms.Get(ctx, dgst, WithTag("latest"))
|
manifest, err = ms.Get(ctx, dgst, distribution.WithTag("latest"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -723,7 +723,7 @@ func TestV1ManifestFetch(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err = ms.Get(ctx, dgst, WithTag("badcontenttype"))
|
manifest, err = ms.Get(ctx, dgst, distribution.WithTag("badcontenttype"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -761,7 +761,7 @@ func TestManifestFetchWithEtag(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("wrong type for client manifest service")
|
panic("wrong type for client manifest service")
|
||||||
}
|
}
|
||||||
_, err = clientManifestService.Get(ctx, d1, WithTag("latest"), AddEtagToTag("latest", d1.String()))
|
_, err = clientManifestService.Get(ctx, d1, distribution.WithTag("latest"), AddEtagToTag("latest", d1.String()))
|
||||||
if err != distribution.ErrManifestNotModified {
|
if err != distribution.ErrManifestNotModified {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -861,7 +861,7 @@ func TestManifestPut(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ms.Put(ctx, m1, WithTag(m1.Tag)); err != nil {
|
if _, err := ms.Put(ctx, m1, distribution.WithTag(m1.Tag)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,11 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err = manifests.Get(imh, imh.Digest)
|
var options []distribution.ManifestServiceOption
|
||||||
|
if imh.Tag != "" {
|
||||||
|
options = append(options, distribution.WithTag(imh.Tag))
|
||||||
|
}
|
||||||
|
manifest, err = manifests.Get(imh, imh.Digest, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithDetail(err))
|
||||||
return
|
return
|
||||||
|
@ -245,7 +249,11 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = manifests.Put(imh, manifest)
|
var options []distribution.ManifestServiceOption
|
||||||
|
if imh.Tag != "" {
|
||||||
|
options = append(options, distribution.WithTag(imh.Tag))
|
||||||
|
}
|
||||||
|
_, err = manifests.Put(imh, manifest, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(stevvooe): These error handling switches really need to be
|
// TODO(stevvooe): These error handling switches really need to be
|
||||||
// handled by an app global mapper.
|
// handled by an app global mapper.
|
||||||
|
|
Loading…
Reference in a new issue