forked from TrueCloudLab/distribution
140 lines
3.7 KiB
Go
140 lines
3.7 KiB
Go
|
package notifications
|
||
|
|
||
|
import (
|
||
|
"time"
|
||
|
|
||
|
"github.com/docker/distribution/manifest"
|
||
|
|
||
|
"code.google.com/p/go-uuid/uuid"
|
||
|
"github.com/docker/distribution/digest"
|
||
|
"github.com/docker/distribution/storage"
|
||
|
)
|
||
|
|
||
|
type bridge struct {
|
||
|
ub URLBuilder
|
||
|
actor ActorRecord
|
||
|
source SourceRecord
|
||
|
sink Sink
|
||
|
}
|
||
|
|
||
|
var _ Listener = &bridge{}
|
||
|
|
||
|
// URLBuilder defines a subset of url builder to be used by the event listener.
|
||
|
type URLBuilder interface {
|
||
|
BuildManifestURL(name, tag string) (string, error)
|
||
|
BuildBlobURL(name string, dgst digest.Digest) (string, error)
|
||
|
}
|
||
|
|
||
|
// NewBridge returns a notification listener that writes records to sink,
|
||
|
// using the actor and source. Any urls populated in the events created by
|
||
|
// this bridge will be created using the URLBuilder.
|
||
|
func NewBridge(ub URLBuilder, source SourceRecord, actor ActorRecord, sink Sink) Listener {
|
||
|
return &bridge{
|
||
|
ub: ub,
|
||
|
actor: actor,
|
||
|
source: source,
|
||
|
sink: sink,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (b *bridge) ManifestPushed(repo storage.Repository, sm *manifest.SignedManifest) error {
|
||
|
return b.createManifestEventAndWrite(EventActionPush, repo, sm)
|
||
|
}
|
||
|
|
||
|
func (b *bridge) ManifestPulled(repo storage.Repository, sm *manifest.SignedManifest) error {
|
||
|
return b.createManifestEventAndWrite(EventActionPull, repo, sm)
|
||
|
}
|
||
|
|
||
|
func (b *bridge) ManifestDeleted(repo storage.Repository, sm *manifest.SignedManifest) error {
|
||
|
return b.createManifestEventAndWrite(EventActionDelete, repo, sm)
|
||
|
}
|
||
|
|
||
|
func (b *bridge) LayerPushed(repo storage.Repository, layer storage.Layer) error {
|
||
|
return b.createLayerEventAndWrite(EventActionPush, repo, layer.Digest())
|
||
|
}
|
||
|
|
||
|
func (b *bridge) LayerPulled(repo storage.Repository, layer storage.Layer) error {
|
||
|
return b.createLayerEventAndWrite(EventActionPull, repo, layer.Digest())
|
||
|
}
|
||
|
|
||
|
func (b *bridge) LayerDeleted(repo storage.Repository, layer storage.Layer) error {
|
||
|
return b.createLayerEventAndWrite(EventActionDelete, repo, layer.Digest())
|
||
|
}
|
||
|
|
||
|
func (b *bridge) createManifestEventAndWrite(action string, repo storage.Repository, sm *manifest.SignedManifest) error {
|
||
|
event, err := b.createManifestEvent(action, repo, sm)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return b.sink.Write(*event)
|
||
|
}
|
||
|
|
||
|
func (b *bridge) createManifestEvent(action string, repo storage.Repository, sm *manifest.SignedManifest) (*Event, error) {
|
||
|
event := b.createEvent(action)
|
||
|
event.Target.Type = "manifest"
|
||
|
event.Target.Name = repo.Name()
|
||
|
event.Target.Tag = sm.Tag
|
||
|
|
||
|
p, err := sm.Payload()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
event.Target.Digest, err = digest.FromBytes(p)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// TODO(stevvooe): Currently, the is the "tag" url: once the digest url is
|
||
|
// implemented, this should be replaced.
|
||
|
event.Target.URL, err = b.ub.BuildManifestURL(sm.Name, sm.Tag)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return event, nil
|
||
|
}
|
||
|
|
||
|
func (b *bridge) createLayerEventAndWrite(action string, repo storage.Repository, dgst digest.Digest) error {
|
||
|
event, err := b.createLayerEvent(action, repo, dgst)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return b.sink.Write(*event)
|
||
|
}
|
||
|
|
||
|
func (b *bridge) createLayerEvent(action string, repo storage.Repository, dgst digest.Digest) (*Event, error) {
|
||
|
event := b.createEvent(action)
|
||
|
event.Target.Type = "layer"
|
||
|
event.Target.Name = repo.Name()
|
||
|
event.Target.Digest = dgst
|
||
|
|
||
|
var err error
|
||
|
event.Target.URL, err = b.ub.BuildBlobURL(repo.Name(), dgst)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return event, nil
|
||
|
}
|
||
|
|
||
|
// createEvent creates an event with actor and source populated.
|
||
|
func (b *bridge) createEvent(action string) *Event {
|
||
|
event := createEvent(action)
|
||
|
event.Source = b.source
|
||
|
event.Actor = b.actor
|
||
|
|
||
|
return event
|
||
|
}
|
||
|
|
||
|
// createEvent returns a new event, timestamped, with the specified action.
|
||
|
func createEvent(action string) *Event {
|
||
|
return &Event{
|
||
|
ID: uuid.New(),
|
||
|
Timestamp: time.Now(),
|
||
|
Action: action,
|
||
|
}
|
||
|
}
|