diff --git a/CHANGELOG.md b/CHANGELOG.md index 6defed4ef..0a60f2900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - support for legacy workspace based on repository name - support for github deployment hooks - provide base sha for github pull requests +- option to filter webhooks by event and type ## [1.2.1] - 2019-06-11 ### Added diff --git a/cmd/drone-server/config/config.go b/cmd/drone-server/config/config.go index 18a0ef629..78650d714 100644 --- a/cmd/drone-server/config/config.go +++ b/cmd/drone-server/config/config.go @@ -280,6 +280,7 @@ type ( // Webhook provides the webhook configuration. Webhook struct { + Events []string `envconfig:"DRONE_WEBHOOK_EVENTS"` Endpoint []string `envconfig:"DRONE_WEBHOOK_ENDPOINT"` Secret string `envconfig:"DRONE_WEBHOOK_SECRET"` SkipVerify bool `envconfig:"DRONE_WEBHOOK_SKIP_VERIFY"` diff --git a/cmd/drone-server/inject_plugin.go b/cmd/drone-server/inject_plugin.go index 7a7510e7d..7e3cd50e8 100644 --- a/cmd/drone-server/inject_plugin.go +++ b/cmd/drone-server/inject_plugin.go @@ -97,6 +97,7 @@ func provideSecretPlugin(config spec.Config) core.SecretService { // a webhook plugin based on the environment configuration. func provideWebhookPlugin(config spec.Config, system *core.System) core.WebhookSender { return webhook.New(webhook.Config{ + Events: config.Webhook.Events, Endpoint: config.Webhook.Endpoint, Secret: config.Webhook.Secret, System: system, diff --git a/core/webhook.go b/core/webhook.go index 1befa0c0d..d4a87b3fd 100644 --- a/core/webhook.go +++ b/core/webhook.go @@ -44,7 +44,7 @@ type ( // WebhookData provides the webhook data. WebhookData struct { - Event string `json:"-"` + Event string `json:"event"` Action string `json:"action"` User *User `json:"user,omitempty"` Repo *Repository `json:"repo,omitempty"` diff --git a/go.mod b/go.mod index 08dc752ed..73e158068 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/docker/distribution v2.7.1+incompatible github.com/docker/go-connections v0.3.0 github.com/docker/go-units v0.3.3 - github.com/drone/drone-go v1.0.5-0.20190427184118-618e4496482e + github.com/drone/drone-go v1.0.5 github.com/drone/drone-runtime v1.0.6 github.com/drone/drone-ui v0.0.0-20190530175131-92ba3df1e0a9 github.com/drone/drone-yaml v1.2.2-0.20190719012529-c50000a465ee @@ -35,7 +35,7 @@ require ( github.com/golang/mock v1.1.1 github.com/golang/protobuf v1.2.0 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c - github.com/google/go-cmp v0.2.0 + github.com/google/go-cmp v0.3.0 github.com/google/go-jsonnet v0.12.1 github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf github.com/google/wire v0.2.1 @@ -43,7 +43,7 @@ require ( github.com/gorhill/cronexpr v0.0.0-20140423231348-a557574d6c02 github.com/gosimple/slug v1.3.0 github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f - github.com/h2non/gock v1.0.10 + github.com/h2non/gock v1.0.15 github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/go-cleanhttp v0.5.0 github.com/hashicorp/go-multierror v1.0.0 @@ -97,3 +97,5 @@ require ( k8s.io/klog v0.1.0 sigs.k8s.io/yaml v1.1.0 ) + +replace github.com/h2non/gock => gopkg.in/h2non/gock.v1 v1.0.14 diff --git a/go.sum b/go.sum index 65d81d368..43bde9d67 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/drone/drone-go v0.0.0-20190217024616-3e8b71333e59/go.mod h1:qVb1k1w9X github.com/drone/drone-go v1.0.4 h1:Yom1lix1Lmk3KmKIsBSQJF1bw0YR2lDGaFQrXxqHMko= github.com/drone/drone-go v1.0.5-0.20190427184118-618e4496482e h1:Z9dJNcs1IpyS52xsf5vjXMq7vSuBt48Il2dJgOzyCFM= github.com/drone/drone-go v1.0.5-0.20190427184118-618e4496482e/go.mod h1:GxyeGClYohaKNYJv/ZpsmVHtMJ7WhoT+uDaJNcDIrk4= +github.com/drone/drone-go v1.0.5 h1:KCDzcPoz5yo5DC4sGcLl+6FsUYbCXjBj9d//b5WP5J0= +github.com/drone/drone-go v1.0.5/go.mod h1:GxyeGClYohaKNYJv/ZpsmVHtMJ7WhoT+uDaJNcDIrk4= github.com/drone/drone-runtime v0.0.0-20190210191445-ad403a0ca24e h1:Eq0QI9lKe6T5pziU/Kes1xX6QKAA6ZfnYvaZZeyY5TU= github.com/drone/drone-runtime v0.0.0-20190210191445-ad403a0ca24e/go.mod h1:I+wJO4yvngCUAro6wKjkMbuPPDI/jRynqU0LTW+8J44= github.com/drone/drone-runtime v1.0.3 h1:0p7ASt0WXbLZRzMOw20e1ahV3YkamRhtZFkm8UvM+JA= @@ -141,6 +143,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCy github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-jsonnet v0.12.1 h1:v0iUm/b4SBz7lR/diMoz9tLAz8lqtnNRKIwMrmU2HEU= github.com/google/go-jsonnet v0.12.1/go.mod h1:gVu3UVSfOt5fRFq+dh9duBqXa5905QY8S1QvMNcEIVs= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= @@ -158,6 +161,7 @@ github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:Fecb github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE= github.com/h2non/gock v1.0.10 h1:EzHYzKKSLN4xk0w193uAy3tp8I3+L1jmaI2Mjg4lCgU= github.com/h2non/gock v1.0.10/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= @@ -202,6 +206,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ= github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4/go.mod h1:cojhOHk1gbMeklOyDP2oKKLftefXoJreOQGOrXk+Z38= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= @@ -270,6 +275,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/plugin/webhook/config.go b/plugin/webhook/config.go index d50315a6d..b712a041d 100644 --- a/plugin/webhook/config.go +++ b/plugin/webhook/config.go @@ -18,6 +18,7 @@ import "github.com/drone/drone/core" // Config provides the webhook configuration. type Config struct { + Events []string Endpoint []string Secret string System *core.System diff --git a/plugin/webhook/webhook.go b/plugin/webhook/webhook.go index d2e7dba64..b5f2fc512 100644 --- a/plugin/webhook/webhook.go +++ b/plugin/webhook/webhook.go @@ -13,6 +13,7 @@ import ( "encoding/base64" "encoding/json" "net/http" + "path/filepath" "time" "github.com/drone/drone/core" @@ -34,6 +35,7 @@ var signer = httpsignatures.NewSigner( // New returns a new Webhook sender. func New(config Config) core.WebhookSender { return &sender{ + Events: config.Events, Endpoints: config.Endpoint, Secret: config.Secret, System: config.System, @@ -47,6 +49,7 @@ type payload struct { type sender struct { Client *http.Client + Events []string Endpoints []string Secret string System *core.System @@ -58,6 +61,9 @@ func (s *sender) Send(ctx context.Context, in *core.WebhookData) error { if len(s.Endpoints) == 0 { return nil } + if s.match(in.Event, in.Action) == false { + return nil + } wrapper := payload{ WebhookData: in, System: s.System, @@ -96,6 +102,25 @@ func (s *sender) send(endpoint, secret, event string, data []byte) error { return err } +func (s *sender) match(event, action string) bool { + if len(s.Events) == 0 { + return true + } + var name string + switch { + case action == "": + name = event + case action != "": + name = event + ":" + action + } + for _, pattern := range s.Events { + if ok, _ := filepath.Match(pattern, name); ok { + return true + } + } + return false +} + func (s *sender) client() *http.Client { if s.Client == nil { return http.DefaultClient diff --git a/plugin/webhook/webhook_test.go b/plugin/webhook/webhook_test.go index f08219aad..46b64047b 100644 --- a/plugin/webhook/webhook_test.go +++ b/plugin/webhook/webhook_test.go @@ -91,3 +91,73 @@ func TestWebhook_NoEndpoints(t *testing.T) { t.Error(err) } } + +func TestWebhook_NoMatch(t *testing.T) { + webhook := &core.WebhookData{ + Event: core.WebhookEventUser, + Action: core.WebhookActionCreated, + User: &core.User{Login: "octocat"}, + } + + config := Config{ + Events: []string{"repo:disabled"}, + Endpoint: []string{"https://localhost:1234"}, + Secret: "correct-horse-battery-staple", + } + sender := New(config) + err := sender.Send(noContext, webhook) + if err != nil { + t.Error(err) + } +} + +func TestWebhook_Match(t *testing.T) { + tests := []struct { + events []string + event string + action string + matched bool + }{ + { + event: "repo", + action: "enabled", + matched: true, + }, + { + events: []string{"user", "repo"}, + event: "repo", + matched: true, + }, + { + events: []string{"repo:disabled", "repo:enabled"}, + event: "repo", + action: "enabled", + matched: true, + }, + { + events: []string{"repo:disabled", "repo:*"}, + event: "repo", + action: "enabled", + matched: true, + }, + { + events: []string{"repo:disabled", "user:created"}, + event: "repo", + action: "enabled", + matched: false, + }, + { + events: []string{"repo", "user"}, + event: "repo", + action: "enabled", + matched: false, + }, + } + for i, test := range tests { + s := new(sender) + s.Events = test.events + if s.match(test.event, test.action) != test.matched { + t.Errorf("Expect matched %v at index %d", test.matched, i) + } + } +}