github.com/lusis/distribution@v2.0.1+incompatible/notifications/bridge.go (about)

     1  package notifications
     2  
     3  import (
     4  	"net/http"
     5  	"time"
     6  
     7  	"code.google.com/p/go-uuid/uuid"
     8  	"github.com/docker/distribution"
     9  	"github.com/docker/distribution/context"
    10  	"github.com/docker/distribution/digest"
    11  	"github.com/docker/distribution/manifest"
    12  )
    13  
    14  type bridge struct {
    15  	ub      URLBuilder
    16  	actor   ActorRecord
    17  	source  SourceRecord
    18  	request RequestRecord
    19  	sink    Sink
    20  }
    21  
    22  var _ Listener = &bridge{}
    23  
    24  // URLBuilder defines a subset of url builder to be used by the event listener.
    25  type URLBuilder interface {
    26  	BuildManifestURL(name, tag string) (string, error)
    27  	BuildBlobURL(name string, dgst digest.Digest) (string, error)
    28  }
    29  
    30  // NewBridge returns a notification listener that writes records to sink,
    31  // using the actor and source. Any urls populated in the events created by
    32  // this bridge will be created using the URLBuilder.
    33  // TODO(stevvooe): Update this to simply take a context.Context object.
    34  func NewBridge(ub URLBuilder, source SourceRecord, actor ActorRecord, request RequestRecord, sink Sink) Listener {
    35  	return &bridge{
    36  		ub:      ub,
    37  		actor:   actor,
    38  		source:  source,
    39  		request: request,
    40  		sink:    sink,
    41  	}
    42  }
    43  
    44  // NewRequestRecord builds a RequestRecord for use in NewBridge from an
    45  // http.Request, associating it with a request id.
    46  func NewRequestRecord(id string, r *http.Request) RequestRecord {
    47  	return RequestRecord{
    48  		ID:        id,
    49  		Addr:      context.RemoteAddr(r),
    50  		Host:      r.Host,
    51  		Method:    r.Method,
    52  		UserAgent: r.UserAgent(),
    53  	}
    54  }
    55  
    56  func (b *bridge) ManifestPushed(repo distribution.Repository, sm *manifest.SignedManifest) error {
    57  	return b.createManifestEventAndWrite(EventActionPush, repo, sm)
    58  }
    59  
    60  func (b *bridge) ManifestPulled(repo distribution.Repository, sm *manifest.SignedManifest) error {
    61  	return b.createManifestEventAndWrite(EventActionPull, repo, sm)
    62  }
    63  
    64  func (b *bridge) ManifestDeleted(repo distribution.Repository, sm *manifest.SignedManifest) error {
    65  	return b.createManifestEventAndWrite(EventActionDelete, repo, sm)
    66  }
    67  
    68  func (b *bridge) LayerPushed(repo distribution.Repository, layer distribution.Layer) error {
    69  	return b.createLayerEventAndWrite(EventActionPush, repo, layer)
    70  }
    71  
    72  func (b *bridge) LayerPulled(repo distribution.Repository, layer distribution.Layer) error {
    73  	return b.createLayerEventAndWrite(EventActionPull, repo, layer)
    74  }
    75  
    76  func (b *bridge) LayerDeleted(repo distribution.Repository, layer distribution.Layer) error {
    77  	return b.createLayerEventAndWrite(EventActionDelete, repo, layer)
    78  }
    79  
    80  func (b *bridge) createManifestEventAndWrite(action string, repo distribution.Repository, sm *manifest.SignedManifest) error {
    81  	manifestEvent, err := b.createManifestEvent(action, repo, sm)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	return b.sink.Write(*manifestEvent)
    87  }
    88  
    89  func (b *bridge) createManifestEvent(action string, repo distribution.Repository, sm *manifest.SignedManifest) (*Event, error) {
    90  	event := b.createEvent(action)
    91  	event.Target.MediaType = manifest.ManifestMediaType
    92  	event.Target.Repository = repo.Name()
    93  
    94  	p, err := sm.Payload()
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	event.Target.Length = int64(len(p))
   100  
   101  	event.Target.Digest, err = digest.FromBytes(p)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	// TODO(stevvooe): Currently, the is the "tag" url: once the digest url is
   107  	// implemented, this should be replaced.
   108  	event.Target.URL, err = b.ub.BuildManifestURL(sm.Name, sm.Tag)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	return event, nil
   114  }
   115  
   116  func (b *bridge) createLayerEventAndWrite(action string, repo distribution.Repository, layer distribution.Layer) error {
   117  	event, err := b.createLayerEvent(action, repo, layer)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	return b.sink.Write(*event)
   123  }
   124  
   125  func (b *bridge) createLayerEvent(action string, repo distribution.Repository, layer distribution.Layer) (*Event, error) {
   126  	event := b.createEvent(action)
   127  	event.Target.MediaType = layerMediaType
   128  	event.Target.Repository = repo.Name()
   129  
   130  	event.Target.Length = layer.Length()
   131  
   132  	dgst := layer.Digest()
   133  	event.Target.Digest = dgst
   134  
   135  	var err error
   136  	event.Target.URL, err = b.ub.BuildBlobURL(repo.Name(), dgst)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	return event, nil
   142  }
   143  
   144  // createEvent creates an event with actor and source populated.
   145  func (b *bridge) createEvent(action string) *Event {
   146  	event := createEvent(action)
   147  	event.Source = b.source
   148  	event.Actor = b.actor
   149  	event.Request = b.request
   150  
   151  	return event
   152  }
   153  
   154  // createEvent returns a new event, timestamped, with the specified action.
   155  func createEvent(action string) *Event {
   156  	return &Event{
   157  		ID:        uuid.New(),
   158  		Timestamp: time.Now(),
   159  		Action:    action,
   160  	}
   161  }