forked from TrueCloudLab/distribution
Merge pull request #239 from jlhawn/event_target_update
notifications: update notification event Target fields
This commit is contained in:
commit
f0ccdd448f
6 changed files with 89 additions and 55 deletions
|
@ -65,37 +65,38 @@ func (b *bridge) ManifestDeleted(repo distribution.Repository, sm *manifest.Sign
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) LayerPushed(repo distribution.Repository, layer distribution.Layer) error {
|
func (b *bridge) LayerPushed(repo distribution.Repository, layer distribution.Layer) error {
|
||||||
return b.createLayerEventAndWrite(EventActionPush, repo, layer.Digest())
|
return b.createLayerEventAndWrite(EventActionPush, repo, layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) LayerPulled(repo distribution.Repository, layer distribution.Layer) error {
|
func (b *bridge) LayerPulled(repo distribution.Repository, layer distribution.Layer) error {
|
||||||
return b.createLayerEventAndWrite(EventActionPull, repo, layer.Digest())
|
return b.createLayerEventAndWrite(EventActionPull, repo, layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) LayerDeleted(repo distribution.Repository, layer distribution.Layer) error {
|
func (b *bridge) LayerDeleted(repo distribution.Repository, layer distribution.Layer) error {
|
||||||
return b.createLayerEventAndWrite(EventActionDelete, repo, layer.Digest())
|
return b.createLayerEventAndWrite(EventActionDelete, repo, layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) createManifestEventAndWrite(action string, repo distribution.Repository, sm *manifest.SignedManifest) error {
|
func (b *bridge) createManifestEventAndWrite(action string, repo distribution.Repository, sm *manifest.SignedManifest) error {
|
||||||
event, err := b.createManifestEvent(action, repo, sm)
|
manifestEvent, err := b.createManifestEvent(action, repo, sm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.sink.Write(*event)
|
return b.sink.Write(*manifestEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) createManifestEvent(action string, repo distribution.Repository, sm *manifest.SignedManifest) (*Event, error) {
|
func (b *bridge) createManifestEvent(action string, repo distribution.Repository, sm *manifest.SignedManifest) (*Event, error) {
|
||||||
event := b.createEvent(action)
|
event := b.createEvent(action)
|
||||||
event.Target.Type = EventTargetTypeManifest
|
event.Target.MediaType = manifest.ManifestMediaType
|
||||||
event.Target.Name = repo.Name()
|
event.Target.Repository = repo.Name()
|
||||||
event.Target.Tag = sm.Tag
|
|
||||||
|
|
||||||
p, err := sm.Payload()
|
p, err := sm.Payload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event.Target.Length = int64(len(p))
|
||||||
|
|
||||||
event.Target.Digest, err = digest.FromBytes(p)
|
event.Target.Digest, err = digest.FromBytes(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -111,8 +112,8 @@ func (b *bridge) createManifestEvent(action string, repo distribution.Repository
|
||||||
return event, nil
|
return event, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) createLayerEventAndWrite(action string, repo distribution.Repository, dgst digest.Digest) error {
|
func (b *bridge) createLayerEventAndWrite(action string, repo distribution.Repository, layer distribution.Layer) error {
|
||||||
event, err := b.createLayerEvent(action, repo, dgst)
|
event, err := b.createLayerEvent(action, repo, layer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -120,10 +121,14 @@ func (b *bridge) createLayerEventAndWrite(action string, repo distribution.Repos
|
||||||
return b.sink.Write(*event)
|
return b.sink.Write(*event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) createLayerEvent(action string, repo distribution.Repository, dgst digest.Digest) (*Event, error) {
|
func (b *bridge) createLayerEvent(action string, repo distribution.Repository, layer distribution.Layer) (*Event, error) {
|
||||||
event := b.createEvent(action)
|
event := b.createEvent(action)
|
||||||
event.Target.Type = EventTargetTypeBlob
|
event.Target.MediaType = layerMediaType
|
||||||
event.Target.Name = repo.Name()
|
event.Target.Repository = repo.Name()
|
||||||
|
|
||||||
|
event.Target.Length = layer.Length()
|
||||||
|
|
||||||
|
dgst := layer.Digest()
|
||||||
event.Target.Digest = dgst
|
event.Target.Digest = dgst
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EventAction constants used in action field of Event.
|
// EventAction constants used in action field of Event.
|
||||||
|
@ -14,17 +14,16 @@ const (
|
||||||
EventActionDelete = "delete"
|
EventActionDelete = "delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EventTargetType constants used in Target section of Event.
|
|
||||||
const (
|
const (
|
||||||
EventTargetTypeManifest = "manifest"
|
// EventsMediaType is the mediatype for the json event envelope. If the
|
||||||
EventTargetTypeBlob = "blob"
|
// Event, ActorRecord, SourceRecord or Envelope structs change, the version
|
||||||
|
// number should be incremented.
|
||||||
|
EventsMediaType = "application/vnd.docker.distribution.events.v1+json"
|
||||||
|
// LayerMediaType is the media type for image rootfs diffs (aka "layers")
|
||||||
|
// used by Docker. We don't expect this to change for quite a while.
|
||||||
|
layerMediaType = "application/vnd.docker.container.image.rootfs.diff+x-gtar"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EventsMediaType is the mediatype for the json event envelope. If the Event,
|
|
||||||
// ActorRecord, SourceRecord or Envelope structs change, the version number
|
|
||||||
// should be incremented.
|
|
||||||
const EventsMediaType = "application/vnd.docker.distribution.events.v1+json"
|
|
||||||
|
|
||||||
// Envelope defines the fields of a json event envelope message that can hold
|
// Envelope defines the fields of a json event envelope message that can hold
|
||||||
// one or more events.
|
// one or more events.
|
||||||
type Envelope struct {
|
type Envelope struct {
|
||||||
|
@ -51,19 +50,14 @@ type Event struct {
|
||||||
|
|
||||||
// Target uniquely describes the target of the event.
|
// Target uniquely describes the target of the event.
|
||||||
Target struct {
|
Target struct {
|
||||||
// Type should be "manifest" or "blob"
|
// TODO(stevvooe): Use http.DetectContentType for layers, maybe.
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
|
|
||||||
// Name identifies the named repository.
|
distribution.Descriptor
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
|
|
||||||
// Digest should identify the object in the repository.
|
// Repository identifies the named repository.
|
||||||
Digest digest.Digest `json:"digest,omitempty"`
|
Repository string `json:"repository,omitempty"`
|
||||||
|
|
||||||
// Tag is present if the operation involved a tagged manifest.
|
// URL provides a direct link to the content.
|
||||||
Tag string `json:"tag,omitempty"`
|
|
||||||
|
|
||||||
// URL provides a link to the content on the relevant repository instance.
|
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
} `json:"target,omitempty"`
|
} `json:"target,omitempty"`
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestEventJSONFormat provides silly test to detect if the event format or
|
// TestEventJSONFormat provides silly test to detect if the event format or
|
||||||
|
@ -19,10 +21,10 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
|
||||||
"timestamp": "2006-01-02T15:04:05Z",
|
"timestamp": "2006-01-02T15:04:05Z",
|
||||||
"action": "push",
|
"action": "push",
|
||||||
"target": {
|
"target": {
|
||||||
"type": "manifest",
|
"mediaType": "application/vnd.docker.distribution.manifest.v1+json",
|
||||||
"name": "library/test",
|
"length": 1,
|
||||||
"digest": "sha256:0123456789abcdef0",
|
"digest": "sha256:0123456789abcdef0",
|
||||||
"tag": "latest",
|
"repository": "library/test",
|
||||||
"url": "http://example.com/v2/library/test/manifests/latest"
|
"url": "http://example.com/v2/library/test/manifests/latest"
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
|
@ -44,9 +46,10 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
|
||||||
"timestamp": "2006-01-02T15:04:05Z",
|
"timestamp": "2006-01-02T15:04:05Z",
|
||||||
"action": "push",
|
"action": "push",
|
||||||
"target": {
|
"target": {
|
||||||
"type": "blob",
|
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
||||||
"name": "library/test",
|
"length": 2,
|
||||||
"digest": "tarsum.v2+sha256:0123456789abcdef1",
|
"digest": "tarsum.v2+sha256:0123456789abcdef1",
|
||||||
|
"repository": "library/test",
|
||||||
"url": "http://example.com/v2/library/test/manifests/latest"
|
"url": "http://example.com/v2/library/test/manifests/latest"
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
|
@ -68,9 +71,10 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
|
||||||
"timestamp": "2006-01-02T15:04:05Z",
|
"timestamp": "2006-01-02T15:04:05Z",
|
||||||
"action": "push",
|
"action": "push",
|
||||||
"target": {
|
"target": {
|
||||||
"type": "blob",
|
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
||||||
"name": "library/test",
|
"length": 3,
|
||||||
"digest": "tarsum.v2+sha256:0123456789abcdef2",
|
"digest": "tarsum.v2+sha256:0123456789abcdef2",
|
||||||
|
"repository": "library/test",
|
||||||
"url": "http://example.com/v2/library/test/manifests/latest"
|
"url": "http://example.com/v2/library/test/manifests/latest"
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
|
@ -97,7 +101,7 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var prototype Event
|
var prototype Event
|
||||||
prototype.Action = "push"
|
prototype.Action = EventActionPush
|
||||||
prototype.Timestamp = tm
|
prototype.Timestamp = tm
|
||||||
prototype.Actor.Name = "test-actor"
|
prototype.Actor.Name = "test-actor"
|
||||||
prototype.Request.ID = "asdfasdf"
|
prototype.Request.ID = "asdfasdf"
|
||||||
|
@ -111,25 +115,27 @@ func TestEventEnvelopeJSONFormat(t *testing.T) {
|
||||||
manifestPush = prototype
|
manifestPush = prototype
|
||||||
manifestPush.ID = "asdf-asdf-asdf-asdf-0"
|
manifestPush.ID = "asdf-asdf-asdf-asdf-0"
|
||||||
manifestPush.Target.Digest = "sha256:0123456789abcdef0"
|
manifestPush.Target.Digest = "sha256:0123456789abcdef0"
|
||||||
manifestPush.Target.Type = EventTargetTypeManifest
|
manifestPush.Target.Length = int64(1)
|
||||||
manifestPush.Target.Name = "library/test"
|
manifestPush.Target.MediaType = manifest.ManifestMediaType
|
||||||
manifestPush.Target.Tag = "latest"
|
manifestPush.Target.Repository = "library/test"
|
||||||
manifestPush.Target.URL = "http://example.com/v2/library/test/manifests/latest"
|
manifestPush.Target.URL = "http://example.com/v2/library/test/manifests/latest"
|
||||||
|
|
||||||
var layerPush0 Event
|
var layerPush0 Event
|
||||||
layerPush0 = prototype
|
layerPush0 = prototype
|
||||||
layerPush0.ID = "asdf-asdf-asdf-asdf-1"
|
layerPush0.ID = "asdf-asdf-asdf-asdf-1"
|
||||||
layerPush0.Target.Digest = "tarsum.v2+sha256:0123456789abcdef1"
|
layerPush0.Target.Digest = "tarsum.v2+sha256:0123456789abcdef1"
|
||||||
layerPush0.Target.Type = EventTargetTypeBlob
|
layerPush0.Target.Length = 2
|
||||||
layerPush0.Target.Name = "library/test"
|
layerPush0.Target.MediaType = layerMediaType
|
||||||
|
layerPush0.Target.Repository = "library/test"
|
||||||
layerPush0.Target.URL = "http://example.com/v2/library/test/manifests/latest"
|
layerPush0.Target.URL = "http://example.com/v2/library/test/manifests/latest"
|
||||||
|
|
||||||
var layerPush1 Event
|
var layerPush1 Event
|
||||||
layerPush1 = prototype
|
layerPush1 = prototype
|
||||||
layerPush1.ID = "asdf-asdf-asdf-asdf-2"
|
layerPush1.ID = "asdf-asdf-asdf-asdf-2"
|
||||||
layerPush1.Target.Digest = "tarsum.v2+sha256:0123456789abcdef2"
|
layerPush1.Target.Digest = "tarsum.v2+sha256:0123456789abcdef2"
|
||||||
layerPush1.Target.Type = EventTargetTypeBlob
|
layerPush1.Target.Length = 3
|
||||||
layerPush1.Target.Name = "library/test"
|
layerPush1.Target.MediaType = layerMediaType
|
||||||
|
layerPush1.Target.Repository = "library/test"
|
||||||
layerPush1.Target.URL = "http://example.com/v2/library/test/manifests/latest"
|
layerPush1.Target.URL = "http://example.com/v2/library/test/manifests/latest"
|
||||||
|
|
||||||
var envelope Envelope
|
var envelope Envelope
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/manifest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestHTTPSink mocks out an http endpoint and notifies it under a couple of
|
// TestHTTPSink mocks out an http endpoint and notifies it under a couple of
|
||||||
|
@ -73,14 +75,14 @@ func TestHTTPSink(t *testing.T) {
|
||||||
{
|
{
|
||||||
statusCode: http.StatusOK,
|
statusCode: http.StatusOK,
|
||||||
events: []Event{
|
events: []Event{
|
||||||
createTestEvent("push", "library/test", "manifest")},
|
createTestEvent("push", "library/test", manifest.ManifestMediaType)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
statusCode: http.StatusOK,
|
statusCode: http.StatusOK,
|
||||||
events: []Event{
|
events: []Event{
|
||||||
createTestEvent("push", "library/test", "manifest"),
|
createTestEvent("push", "library/test", manifest.ManifestMediaType),
|
||||||
createTestEvent("push", "library/test", "layer"),
|
createTestEvent("push", "library/test", layerMediaType),
|
||||||
createTestEvent("push", "library/test", "layer"),
|
createTestEvent("push", "library/test", layerMediaType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -148,8 +150,8 @@ func TestHTTPSink(t *testing.T) {
|
||||||
func createTestEvent(action, repo, typ string) Event {
|
func createTestEvent(action, repo, typ string) Event {
|
||||||
event := createEvent(action)
|
event := createEvent(action)
|
||||||
|
|
||||||
event.Target.Type = typ
|
event.Target.MediaType = typ
|
||||||
event.Target.Name = repo
|
event.Target.Repository = repo
|
||||||
|
|
||||||
return *event
|
return *event
|
||||||
}
|
}
|
||||||
|
|
27
registry.go
27
registry.go
|
@ -98,10 +98,12 @@ type Layer interface {
|
||||||
io.ReadSeeker
|
io.ReadSeeker
|
||||||
io.Closer
|
io.Closer
|
||||||
|
|
||||||
// Digest returns the unique digest of the blob, which is the tarsum for
|
// Digest returns the unique digest of the blob.
|
||||||
// layers.
|
|
||||||
Digest() digest.Digest
|
Digest() digest.Digest
|
||||||
|
|
||||||
|
// Length returns the length in bytes of the blob.
|
||||||
|
Length() int64
|
||||||
|
|
||||||
// CreatedAt returns the time this layer was created.
|
// CreatedAt returns the time this layer was created.
|
||||||
CreatedAt() time.Time
|
CreatedAt() time.Time
|
||||||
}
|
}
|
||||||
|
@ -137,3 +139,24 @@ type SignatureService interface {
|
||||||
// Put stores the signature for the provided digest.
|
// Put stores the signature for the provided digest.
|
||||||
Put(dgst digest.Digest, signatures ...[]byte) error
|
Put(dgst digest.Digest, signatures ...[]byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Descriptor describes targeted content. Used in conjunction with a blob
|
||||||
|
// store, a descriptor can be used to fetch, store and target any kind of
|
||||||
|
// blob. The struct also describes the wire protocol format. Fields should
|
||||||
|
// only be added but never changed.
|
||||||
|
type Descriptor struct {
|
||||||
|
// MediaType describe the type of the content. All text based formats are
|
||||||
|
// encoded as utf-8.
|
||||||
|
MediaType string `json:"mediaType,omitempty"`
|
||||||
|
|
||||||
|
// Length in bytes of content.
|
||||||
|
Length int64 `json:"length,omitempty"`
|
||||||
|
|
||||||
|
// Digest uniquely identifies the content. A byte stream can be verified
|
||||||
|
// against against this digest.
|
||||||
|
Digest digest.Digest `json:"digest,omitempty"`
|
||||||
|
|
||||||
|
// NOTE: Before adding a field here, please ensure that all
|
||||||
|
// other options have been exhausted. Much of the type relationships
|
||||||
|
// depend on the simplicity of this type.
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@ func (lrs *layerReader) Digest() digest.Digest {
|
||||||
return lrs.digest
|
return lrs.digest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lrs *layerReader) Length() int64 {
|
||||||
|
return lrs.size
|
||||||
|
}
|
||||||
|
|
||||||
func (lrs *layerReader) CreatedAt() time.Time {
|
func (lrs *layerReader) CreatedAt() time.Time {
|
||||||
return lrs.modtime
|
return lrs.modtime
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue