4441333912
Most places in the registry were using string types to refer to repository names. This changes them to use reference.Named, so the type system can enforce validation of the naming rules. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
167 lines
4.6 KiB
Go
167 lines
4.6 KiB
Go
package notifications
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/digest"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/distribution/uuid"
|
|
)
|
|
|
|
type bridge struct {
|
|
ub URLBuilder
|
|
actor ActorRecord
|
|
source SourceRecord
|
|
request RequestRecord
|
|
sink Sink
|
|
}
|
|
|
|
var _ Listener = &bridge{}
|
|
|
|
// URLBuilder defines a subset of url builder to be used by the event listener.
|
|
type URLBuilder interface {
|
|
BuildManifestURL(name reference.Named, tag string) (string, error)
|
|
BuildBlobURL(name reference.Named, 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.
|
|
// TODO(stevvooe): Update this to simply take a context.Context object.
|
|
func NewBridge(ub URLBuilder, source SourceRecord, actor ActorRecord, request RequestRecord, sink Sink) Listener {
|
|
return &bridge{
|
|
ub: ub,
|
|
actor: actor,
|
|
source: source,
|
|
request: request,
|
|
sink: sink,
|
|
}
|
|
}
|
|
|
|
// NewRequestRecord builds a RequestRecord for use in NewBridge from an
|
|
// http.Request, associating it with a request id.
|
|
func NewRequestRecord(id string, r *http.Request) RequestRecord {
|
|
return RequestRecord{
|
|
ID: id,
|
|
Addr: context.RemoteAddr(r),
|
|
Host: r.Host,
|
|
Method: r.Method,
|
|
UserAgent: r.UserAgent(),
|
|
}
|
|
}
|
|
|
|
func (b *bridge) ManifestPushed(repo reference.Named, sm distribution.Manifest) error {
|
|
return b.createManifestEventAndWrite(EventActionPush, repo, sm)
|
|
}
|
|
|
|
func (b *bridge) ManifestPulled(repo reference.Named, sm distribution.Manifest) error {
|
|
return b.createManifestEventAndWrite(EventActionPull, repo, sm)
|
|
}
|
|
|
|
func (b *bridge) ManifestDeleted(repo reference.Named, sm distribution.Manifest) error {
|
|
return b.createManifestEventAndWrite(EventActionDelete, repo, sm)
|
|
}
|
|
|
|
func (b *bridge) BlobPushed(repo reference.Named, desc distribution.Descriptor) error {
|
|
return b.createBlobEventAndWrite(EventActionPush, repo, desc)
|
|
}
|
|
|
|
func (b *bridge) BlobPulled(repo reference.Named, desc distribution.Descriptor) error {
|
|
return b.createBlobEventAndWrite(EventActionPull, repo, desc)
|
|
}
|
|
|
|
func (b *bridge) BlobMounted(repo reference.Named, desc distribution.Descriptor, fromRepo reference.Named) error {
|
|
event, err := b.createBlobEvent(EventActionMount, repo, desc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
event.Target.FromRepository = fromRepo.Name()
|
|
return b.sink.Write(*event)
|
|
}
|
|
|
|
func (b *bridge) BlobDeleted(repo reference.Named, desc distribution.Descriptor) error {
|
|
return b.createBlobEventAndWrite(EventActionDelete, repo, desc)
|
|
}
|
|
|
|
func (b *bridge) createManifestEventAndWrite(action string, repo reference.Named, sm distribution.Manifest) error {
|
|
manifestEvent, err := b.createManifestEvent(action, repo, sm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return b.sink.Write(*manifestEvent)
|
|
}
|
|
|
|
func (b *bridge) createManifestEvent(action string, repo reference.Named, sm distribution.Manifest) (*Event, error) {
|
|
event := b.createEvent(action)
|
|
event.Target.Repository = repo.Name()
|
|
|
|
mt, p, err := sm.Payload()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Ensure we have the canonical manifest descriptor here
|
|
_, desc, err := distribution.UnmarshalManifest(mt, p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
event.Target.MediaType = mt
|
|
event.Target.Length = desc.Size
|
|
event.Target.Size = desc.Size
|
|
event.Target.Digest = desc.Digest
|
|
|
|
event.Target.URL, err = b.ub.BuildManifestURL(repo, event.Target.Digest.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return event, nil
|
|
}
|
|
|
|
func (b *bridge) createBlobEventAndWrite(action string, repo reference.Named, desc distribution.Descriptor) error {
|
|
event, err := b.createBlobEvent(action, repo, desc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return b.sink.Write(*event)
|
|
}
|
|
|
|
func (b *bridge) createBlobEvent(action string, repo reference.Named, desc distribution.Descriptor) (*Event, error) {
|
|
event := b.createEvent(action)
|
|
event.Target.Descriptor = desc
|
|
event.Target.Length = desc.Size
|
|
event.Target.Repository = repo.Name()
|
|
|
|
var err error
|
|
event.Target.URL, err = b.ub.BuildBlobURL(repo, desc.Digest)
|
|
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
|
|
event.Request = b.request
|
|
|
|
return event
|
|
}
|
|
|
|
// createEvent returns a new event, timestamped, with the specified action.
|
|
func createEvent(action string) *Event {
|
|
return &Event{
|
|
ID: uuid.Generate().String(),
|
|
Timestamp: time.Now(),
|
|
Action: action,
|
|
}
|
|
}
|