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 }