Merge pull request #239 from jlhawn/event_target_update

notifications: update notification event Target fields
This commit is contained in:
Stephen Day 2015-03-06 16:45:06 -08:00
commit f0ccdd448f
6 changed files with 89 additions and 55 deletions

View file

@ -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

View file

@ -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"`

View file

@ -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

View file

@ -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
} }

View file

@ -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.
}

View file

@ -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
} }