diff --git a/configuration/configuration.go b/configuration/configuration.go index cdc996b9..708c4b51 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -552,6 +552,13 @@ type Endpoint struct { Threshold int `yaml:"threshold"` // circuit breaker threshold before backing off on failure Backoff time.Duration `yaml:"backoff"` // backoff duration IgnoredMediaTypes []string `yaml:"ignoredmediatypes"` // target media types to ignore + Ignore Ignore `yaml:"ignore"` // ignore event types +} + +//Ignore configures mediaTypes and actions of the event, that it won't be propagated +type Ignore struct { + MediaTypes []string `yaml:"mediatypes"` // target media types to ignore + Actions []string `yaml:"actions"` // ignore action types } // Reporting defines error reporting methods. diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index 3e1583dd..a8ec640e 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -63,6 +63,10 @@ var configStruct = Configuration{ "Authorization": []string{"Bearer "}, }, IgnoredMediaTypes: []string{"application/octet-stream"}, + Ignore: Ignore{ + MediaTypes: []string{"application/octet-stream"}, + Actions: []string{"pull"}, + }, }, }, }, @@ -142,6 +146,11 @@ notifications: Authorization: [Bearer ] ignoredmediatypes: - application/octet-stream + ignore: + mediatypes: + - application/octet-stream + actions: + - pull reporting: bugsnag: apikey: BugsnagApiKey @@ -170,6 +179,11 @@ notifications: Authorization: [Bearer ] ignoredmediatypes: - application/octet-stream + ignore: + mediatypes: + - application/octet-stream + actions: + - pull http: headers: X-Content-Type-Options: [nosniff] diff --git a/docs/configuration.md b/docs/configuration.md index 807353cc..3eacfc50 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -232,6 +232,11 @@ notifications: backoff: 1s ignoredmediatypes: - application/octet-stream + ignore: + mediatypes: + - application/octet-stream + actions: + - pull redis: addr: localhost:6379 password: asecret @@ -837,6 +842,11 @@ notifications: backoff: 1s ignoredmediatypes: - application/octet-stream + ignore: + mediatypes: + - application/octet-stream + actions: + - pull ``` The notifications option is **optional** and currently may contain a single @@ -857,6 +867,14 @@ accept event notifications. | `threshold` | yes | An integer specifying how long to wait before backing off a failure. | | `backoff` | yes | How long the system backs off before retrying after a failure. A positive integer and an optional suffix indicating the unit of time, which may be `ns`, `us`, `ms`, `s`, `m`, or `h`. If you omit the unit of time, `ns` is used. | | `ignoredmediatypes`|no| A list of target media types to ignore. Events with these target media types are not published to the endpoint. | +| `ignore` |no| Events with these mediatypes or actions are not published to the endpoint. | + +#### `ignore` +| Parameter | Required | Description | +|-----------|----------|-------------------------------------------------------| +| `mediatypes`|no| A list of target media types to ignore. Events with these target media types are not published to the endpoint. | +| `actions` |no| A list of actions to ignore. Events with these actions are not published to the endpoint. | + ## `redis` diff --git a/notifications/endpoint.go b/notifications/endpoint.go index 44d0f6d7..285995ec 100644 --- a/notifications/endpoint.go +++ b/notifications/endpoint.go @@ -1,6 +1,7 @@ package notifications import ( + "github.com/docker/distribution/configuration" "net/http" "time" ) @@ -14,6 +15,7 @@ type EndpointConfig struct { Backoff time.Duration IgnoredMediaTypes []string Transport *http.Transport `json:"-"` + Ignore configuration.Ignore } // defaults set any zero-valued fields to a reasonable default. @@ -63,7 +65,8 @@ func NewEndpoint(name, url string, config EndpointConfig) *Endpoint { endpoint.Transport, endpoint.metrics.httpStatusListener()) endpoint.Sink = newRetryingSink(endpoint.Sink, endpoint.Threshold, endpoint.Backoff) endpoint.Sink = newEventQueue(endpoint.Sink, endpoint.metrics.eventQueueListener()) - endpoint.Sink = newIgnoredMediaTypesSink(endpoint.Sink, config.IgnoredMediaTypes) + mediaTypes := append(config.Ignore.MediaTypes, config.IgnoredMediaTypes...) + endpoint.Sink = newIgnoredSink(endpoint.Sink, mediaTypes, config.Ignore.Actions) register(&endpoint) return &endpoint diff --git a/notifications/sinks.go b/notifications/sinks.go index e1523df7..14b692a3 100644 --- a/notifications/sinks.go +++ b/notifications/sinks.go @@ -210,14 +210,15 @@ func (eq *eventQueue) next() []Event { return block } -// ignoredMediaTypesSink discards events with ignored target media types and +// ignoredSink discards events with ignored target media types and actions. // passes the rest along. -type ignoredMediaTypesSink struct { +type ignoredSink struct { Sink - ignored map[string]bool + ignoreMediaTypes map[string]bool + ignoreActions map[string]bool } -func newIgnoredMediaTypesSink(sink Sink, ignored []string) Sink { +func newIgnoredSink(sink Sink, ignored []string, ignoreActions []string) Sink { if len(ignored) == 0 { return sink } @@ -227,25 +228,41 @@ func newIgnoredMediaTypesSink(sink Sink, ignored []string) Sink { ignoredMap[mediaType] = true } - return &ignoredMediaTypesSink{ - Sink: sink, - ignored: ignoredMap, + ignoredActionsMap := make(map[string]bool) + for _, action := range ignoreActions { + ignoredActionsMap[action] = true + } + + return &ignoredSink{ + Sink: sink, + ignoreMediaTypes: ignoredMap, + ignoreActions: ignoredActionsMap, } } // Write discards events with ignored target media types and passes the rest // along. -func (imts *ignoredMediaTypesSink) Write(events ...Event) error { +func (imts *ignoredSink) Write(events ...Event) error { var kept []Event for _, e := range events { - if !imts.ignored[e.Target.MediaType] { + if !imts.ignoreMediaTypes[e.Target.MediaType] { kept = append(kept, e) } } if len(kept) == 0 { return nil } - return imts.Sink.Write(kept...) + + var results []Event + for _, e := range kept { + if !imts.ignoreActions[e.Action] { + results = append(results, e) + } + } + if len(results) == 0 { + return nil + } + return imts.Sink.Write(results...) } // retryingSink retries the write until success or an ErrSinkClosed is diff --git a/notifications/sinks_test.go b/notifications/sinks_test.go index 2a85c4f4..9613fe8a 100644 --- a/notifications/sinks_test.go +++ b/notifications/sinks_test.go @@ -113,25 +113,29 @@ func TestEventQueue(t *testing.T) { } } -func TestIgnoredMediaTypesSink(t *testing.T) { +func TestIgnoredSink(t *testing.T) { blob := createTestEvent("push", "library/test", "blob") - manifest := createTestEvent("push", "library/test", "manifest") + manifest := createTestEvent("pull", "library/test", "manifest") type testcase struct { - ignored []string - expected []Event + ignoreMediaTypes []string + ignoreActions []string + expected []Event } cases := []testcase{ - {nil, []Event{blob, manifest}}, - {[]string{"other"}, []Event{blob, manifest}}, - {[]string{"blob"}, []Event{manifest}}, - {[]string{"blob", "manifest"}, nil}, + {nil, nil, []Event{blob, manifest}}, + {[]string{"other"}, []string{"other"}, []Event{blob, manifest}}, + {[]string{"blob"}, []string{"other"}, []Event{manifest}}, + {[]string{"blob", "manifest"}, []string{"other"}, nil}, + {[]string{"other"}, []string{"push"}, []Event{manifest}}, + {[]string{"other"}, []string{"pull"}, []Event{blob}}, + {[]string{"other"}, []string{"pull", "push"}, nil}, } for _, c := range cases { ts := &testSink{} - s := newIgnoredMediaTypesSink(ts, c.ignored) + s := newIgnoredSink(ts, c.ignoreMediaTypes, c.ignoreActions) if err := s.Write(blob, manifest); err != nil { t.Fatalf("error writing event: %v", err) diff --git a/registry/handlers/app.go b/registry/handlers/app.go index e38050cb..0243dd5e 100644 --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -439,6 +439,7 @@ func (app *App) configureEvents(configuration *configuration.Configuration) { Backoff: endpoint.Backoff, Headers: endpoint.Headers, IgnoredMediaTypes: endpoint.IgnoredMediaTypes, + Ignore: endpoint.Ignore, }) sinks = append(sinks, endpoint)