From c6cdbe5806261bc1f3e3361c14124eea739e63b4 Mon Sep 17 00:00:00 2001 From: alonisser Date: Wed, 18 Apr 2018 20:41:10 +0300 Subject: [PATCH 1/2] Adding support for azure webhooks --- trigger/http/azure_webhook_trigger.go | 93 ++++++++++++++++++++++ trigger/http/azure_webhook_trigger_test.go | 73 +++++++++++++++++ trigger/http/http.go | 4 + 3 files changed, 170 insertions(+) create mode 100644 trigger/http/azure_webhook_trigger.go create mode 100644 trigger/http/azure_webhook_trigger_test.go diff --git a/trigger/http/azure_webhook_trigger.go b/trigger/http/azure_webhook_trigger.go new file mode 100644 index 00000000..68882da7 --- /dev/null +++ b/trigger/http/azure_webhook_trigger.go @@ -0,0 +1,93 @@ +package http + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/keel-hq/keel/types" + "github.com/prometheus/client_golang/prometheus" + + log "github.com/sirupsen/logrus" +) + +var newAzureWebhooksCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "azure_webhook_requests_total", + Help: "How many /v1/webhooks/azure requests processed, partitioned by image.", + }, + []string{"image"}, +) + +func init() { + prometheus.MustRegister(newAzureWebhooksCounter) +} + +// Example of azure trigger +// { +// "id": "cb8c3971-9adc-488b-bdd8-43cbb4974ff5", +// "timestamp": "2017-11-17T16:52:01.343145347Z", +// "action": "push", +// "target": { +// "mediaType": "application/vnd.docker.distribution.manifest.v2+json", +// "size": 524, +// "digest": "sha256:80f0d5c8786bb9e621a45ece0db56d11cdc624ad20da9fe62e9d25490f331d7d", +// "length": 524, +// "repository": "hello-world", +// "tag": "v1" +// }, +// "request": { +// "id": "3cbb6949-7549-4fa1-86cd-a6d5451dffc7", +// "host": "myregistry.azurecr.io", +// "method": "PUT", +// "useragent": "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/4.10.0-27-generic os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \\(linux\\))" +// } +//} + +type azureWebhook struct { + Target struct { + Repository string `json:"repository"` + Tag string `json:"tag"` + } `json:"target"` + Request struct { + Host string `json:"host"` + } `json:"request"` +} + +func (s *TriggerServer) azureHandler(resp http.ResponseWriter, req *http.Request) { + qw := azureWebhook{} + if err := json.NewDecoder(req.Body).Decode(&qw); err != nil { + log.WithFields(log.Fields{ + "error": err, + }).Error("trigger.azureHandler: failed to decode request") + resp.WriteHeader(http.StatusBadRequest) + return + } + + //if qw.DockerURL == "" { + // resp.WriteHeader(http.StatusBadRequest) + // fmt.Fprintf(resp, "docker_url cannot be empty") + // return + //} + + if qw.Target.Tag == "" { + resp.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(resp, "tag cannot be empty") + return + } + + // for every updated tag generating event + var DockerURL = qw.Request.Host + "/" + qw.Target.Repository + event := types.Event{} + event.CreatedAt = time.Now() + event.TriggerName = "azure" + event.Repository.Name = DockerURL // need to build this url.. + event.Repository.Tag = qw.Target.Tag + + s.trigger(event) + newAzureWebhooksCounter.With(prometheus.Labels{"image": event.Repository.Name}).Inc() + + resp.WriteHeader(http.StatusOK) + return +} diff --git a/trigger/http/azure_webhook_trigger_test.go b/trigger/http/azure_webhook_trigger_test.go new file mode 100644 index 00000000..86adee8c --- /dev/null +++ b/trigger/http/azure_webhook_trigger_test.go @@ -0,0 +1,73 @@ +package http + +import ( + "bytes" + "net/http" + "time" + + "github.com/keel-hq/keel/approvals" + "github.com/keel-hq/keel/cache/memory" + "github.com/keel-hq/keel/provider" + "github.com/keel-hq/keel/util/codecs" + + "net/http/httptest" + "testing" +) + +var fakeAzureWebhook = `{ + "id": "cb8c3971-9adc-488b-bdd8-43cbb4974ff5", + "timestamp": "2017-11-17T16:52:01.343145347Z", + "action": "push", + "target": { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 524, + "digest": "sha256:80f0d5c8786bb9e621a45ece0db56d11cdc624ad20da9fe62e9d25490f331d7d", + "length": 524, + "repository": "hello-world", + "tag": "v1" + }, + "request": { + "id": "3cbb6949-7549-4fa1-86cd-a6d5451dffc7", + "host": "myregistry.azurecr.io", + "method": "PUT", + "useragent": "docker/17.09.0-ce go/go1.8.3 git-commit/afdb6d4 kernel/4.10.0-27-generic os/linux arch/amd64 UpstreamClient(Docker-Client/17.09.0-ce \\(linux\\))" + } +} +` + +func TestAzureWebhookHandler(t *testing.T) { + + fp := &fakeProvider{} + mem := memory.NewMemoryCache(100*time.Millisecond, 100*time.Millisecond, 10*time.Millisecond) + am := approvals.New(mem, codecs.DefaultSerializer()) + providers := provider.New([]provider.Provider{fp}, am) + srv := NewTriggerServer(&Opts{Providers: providers}) + srv.registerRoutes(srv.router) + + req, err := http.NewRequest("POST", "/v1/webhooks/azure", bytes.NewBuffer([]byte(fakeAzureWebhook))) + if err != nil { + t.Fatalf("failed to create req: %s", err) + } + + //The response recorder used to record HTTP responses + rec := httptest.NewRecorder() + + srv.router.ServeHTTP(rec, req) + if rec.Code != 200 { + t.Errorf("unexpected status code: %d", rec.Code) + + t.Log(rec.Body.String()) + } + + if len(fp.submitted) != 1 { + t.Fatalf("unexpected number of events submitted: %d", len(fp.submitted)) + } + + if fp.submitted[0].Repository.Name != "myregistry.azurecr.io/hello-world" { + t.Errorf("myregistry.azurecr.io/hello-world but got %s", fp.submitted[0].Repository.Name) + } + + if fp.submitted[0].Repository.Tag != "v1" { + t.Errorf("expected v1 but got %s", fp.submitted[0].Repository.Tag) + } +} diff --git a/trigger/http/http.go b/trigger/http/http.go index c352b4a1..c31f6ae4 100644 --- a/trigger/http/http.go +++ b/trigger/http/http.go @@ -100,8 +100,12 @@ func (s *TriggerServer) registerRoutes(mux *mux.Router) { // dockerhub webhooks handler mux.HandleFunc("/v1/webhooks/dockerhub", s.dockerHubHandler).Methods("POST", "OPTIONS") + + // quay webhooks handler mux.HandleFunc("/v1/webhooks/quay", s.quayHandler).Methods("POST", "OPTIONS") + mux.HandleFunc("/v1/webhooks/azure", s.azureHandler).Methods("POST", "OPTIONS") + mux.Handle("/metrics", promhttp.Handler()) } From d1e813bc9274960ef3615fc9791d6f29ed7abf9b Mon Sep 17 00:00:00 2001 From: alonisser Date: Wed, 18 Apr 2018 20:43:26 +0300 Subject: [PATCH 2/2] Remove comment --- trigger/http/azure_webhook_trigger.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/trigger/http/azure_webhook_trigger.go b/trigger/http/azure_webhook_trigger.go index 68882da7..b1d2d287 100644 --- a/trigger/http/azure_webhook_trigger.go +++ b/trigger/http/azure_webhook_trigger.go @@ -65,12 +65,6 @@ func (s *TriggerServer) azureHandler(resp http.ResponseWriter, req *http.Request return } - //if qw.DockerURL == "" { - // resp.WriteHeader(http.StatusBadRequest) - // fmt.Fprintf(resp, "docker_url cannot be empty") - // return - //} - if qw.Target.Tag == "" { resp.WriteHeader(http.StatusBadRequest) fmt.Fprintf(resp, "tag cannot be empty")