From 080e329cb1dfa559a758b78ab18942243a1d3924 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Tue, 3 Feb 2015 13:28:10 -0800 Subject: [PATCH] Separate request data from actor in Event To clarify the role of actor, the request data that initiates an event has been separated. The ActorRecord is pared down to just the username. This eliminates confusion about where event related data should be added. Signed-off-by: Stephen J Day --- registry/app.go | 8 +++--- storage/notifications/bridge.go | 35 +++++++++++++++++++------- storage/notifications/event.go | 36 ++++++++++++++++++--------- storage/notifications/event_test.go | 38 ++++++++++++++++++++--------- 4 files changed, 79 insertions(+), 38 deletions(-) diff --git a/registry/app.go b/registry/app.go index e7c96b74..53759a1e 100644 --- a/registry/app.go +++ b/registry/app.go @@ -336,13 +336,11 @@ func (app *App) eventBridge(ctx *Context, r *http.Request) notifications.Listene // auth system. Would prefer to do this during logging refactor and // addition of user and google context type. actor := notifications.ActorRecord{ - Name: "--todo--", - Addr: r.RemoteAddr, - Host: r.Host, - RequestID: ctx.RequestID, + Name: "--todo--", } + request := notifications.NewRequestRecord(ctx.RequestID, r) - return notifications.NewBridge(ctx.urlBuilder, app.events.source, actor, app.events.sink) + return notifications.NewBridge(ctx.urlBuilder, app.events.source, actor, request, app.events.sink) } // apiBase implements a simple yes-man for doing overall checks against the diff --git a/storage/notifications/bridge.go b/storage/notifications/bridge.go index 28326cce..d6f41ba0 100644 --- a/storage/notifications/bridge.go +++ b/storage/notifications/bridge.go @@ -1,6 +1,7 @@ package notifications import ( + "net/http" "time" "github.com/docker/distribution/manifest" @@ -11,10 +12,11 @@ import ( ) type bridge struct { - ub URLBuilder - actor ActorRecord - source SourceRecord - sink Sink + ub URLBuilder + actor ActorRecord + source SourceRecord + request RequestRecord + sink Sink } var _ Listener = &bridge{} @@ -28,12 +30,26 @@ type URLBuilder interface { // 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 { +// 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, - sink: sink, + 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: r.RemoteAddr, + Host: r.Host, + Method: r.Method, + UserAgent: r.UserAgent(), } } @@ -125,6 +141,7 @@ func (b *bridge) createEvent(action string) *Event { event := createEvent(action) event.Source = b.source event.Actor = b.actor + event.Request = b.request return event } diff --git a/storage/notifications/event.go b/storage/notifications/event.go index fb2094d7..c23766fa 100644 --- a/storage/notifications/event.go +++ b/storage/notifications/event.go @@ -67,6 +67,9 @@ type Event struct { URL string `json:"url,omitempty"` } `json:"target,omitempty"` + // Request covers the request that generated the event. + Request RequestRecord `json:"request,omitempty"` + // Actor specifies the agent that initiated the event. For most // situations, this could be from the authorizaton context of the request. Actor ActorRecord `json:"actor,omitempty"` @@ -86,18 +89,6 @@ type ActorRecord struct { // request context that generated the event. Name string `json:"name,omitempty"` - // Addr contains the ip or hostname and possibly port of the client - // connection that initiated the event. - Addr string `json:"addr,omitempty"` - - // Host is the externally accessible host name of the registry instance, - // as specified by the http host header on incoming requests. - Host string `json:"host,omitempty"` - - // RequestID uniquely identifies the registry request that generated the - // event. - RequestID string `json:"requestID,omitempty"` - // TODO(stevvooe): Look into setting a session cookie to get this // without docker daemon. // SessionID @@ -107,6 +98,27 @@ type ActorRecord struct { // Command } +// RequestRecord covers the request that generated the event. +type RequestRecord struct { + // ID uniquely identifies the request that initiated the event. + ID string `json:"id"` + + // Addr contains the ip or hostname and possibly port of the client + // connection that initiated the event. This is the RemoteAddr from + // the standard http request. + Addr string `json:"addr,omitempty"` + + // Host is the externally accessible host name of the registry instance, + // as specified by the http host header on incoming requests. + Host string `json:"host,omitempty"` + + // Method has the request method that generated the event. + Method string `json:"method"` + + // UserAgent contains the user agent header of the request. + UserAgent string `json:"useragent"` +} + // SourceRecord identifies the registry node that generated the event. Put // differently, while the actor "initiates" the event, the source "generates" // it. diff --git a/storage/notifications/event_test.go b/storage/notifications/event_test.go index 7bb9fa01..cc2180ac 100644 --- a/storage/notifications/event_test.go +++ b/storage/notifications/event_test.go @@ -25,11 +25,15 @@ func TestEventEnvelopeJSONFormat(t *testing.T) { "tag": "latest", "url": "http://example.com/v2/library/test/manifests/latest" }, - "actor": { - "name": "test-actor", + "request": { + "id": "asdfasdf", "addr": "client.local", "host": "registrycluster.local", - "requestID": "asdfasdf" + "method": "PUT", + "useragent": "test/0.1" + }, + "actor": { + "name": "test-actor" }, "source": { "addr": "hostname.local:port" @@ -45,11 +49,15 @@ func TestEventEnvelopeJSONFormat(t *testing.T) { "digest": "tarsum.v2+sha256:0123456789abcdef1", "url": "http://example.com/v2/library/test/manifests/latest" }, - "actor": { - "name": "test-actor", + "request": { + "id": "asdfasdf", "addr": "client.local", "host": "registrycluster.local", - "requestID": "asdfasdf" + "method": "PUT", + "useragent": "test/0.1" + }, + "actor": { + "name": "test-actor" }, "source": { "addr": "hostname.local:port" @@ -65,11 +73,15 @@ func TestEventEnvelopeJSONFormat(t *testing.T) { "digest": "tarsum.v2+sha256:0123456789abcdef2", "url": "http://example.com/v2/library/test/manifests/latest" }, - "actor": { - "name": "test-actor", + "request": { + "id": "asdfasdf", "addr": "client.local", "host": "registrycluster.local", - "requestID": "asdfasdf" + "method": "PUT", + "useragent": "test/0.1" + }, + "actor": { + "name": "test-actor" }, "source": { "addr": "hostname.local:port" @@ -87,10 +99,12 @@ func TestEventEnvelopeJSONFormat(t *testing.T) { var prototype Event prototype.Action = "push" prototype.Timestamp = tm - prototype.Actor.Addr = "client.local" prototype.Actor.Name = "test-actor" - prototype.Actor.RequestID = "asdfasdf" - prototype.Actor.Host = "registrycluster.local" + prototype.Request.ID = "asdfasdf" + prototype.Request.Addr = "client.local" + prototype.Request.Host = "registrycluster.local" + prototype.Request.Method = "PUT" + prototype.Request.UserAgent = "test/0.1" prototype.Source.Addr = "hostname.local:port" var manifestPush Event