From f4d21956d3cddb5daca2e0e63f11a13513bc4a03 Mon Sep 17 00:00:00 2001 From: Furkan-Gulsen Date: Sat, 18 Nov 2023 22:11:56 +0300 Subject: [PATCH 1/7] rebirth --- .env-example | 1 - LICENSE | 2 +- README.md | 53 +- cmd/main.go | 37 - docker-compose.yml | 68 - go.mod | 10 - go.sum | 240 ---- metrics/grafana/dashboards/watermill.json | 1350 -------------------- metrics/grafana/dashboards/webhooks.json | 180 --- metrics/grafana/datasources/prometheus.yml | 6 - metrics/grafana/provisioning/default.yml | 10 - metrics/prometheus.yml | 9 - pkg/amqp.go | 42 - pkg/config.go | 62 - pkg/events.go | 14 - pkg/github.go | 55 - pkg/grafana.go | 109 -- pkg/router.go | 141 -- pkg/simulator.go | 54 - pkg/slack.go | 74 -- scripts/send-stub-webhook.sh | 20 - scripts/wait-for-it.sh | 178 --- scripts/wait.sh | 6 - 23 files changed, 2 insertions(+), 2719 deletions(-) delete mode 100644 .env-example delete mode 100644 cmd/main.go delete mode 100644 docker-compose.yml delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 metrics/grafana/dashboards/watermill.json delete mode 100644 metrics/grafana/dashboards/webhooks.json delete mode 100644 metrics/grafana/datasources/prometheus.yml delete mode 100644 metrics/grafana/provisioning/default.yml delete mode 100644 metrics/prometheus.yml delete mode 100644 pkg/amqp.go delete mode 100644 pkg/config.go delete mode 100644 pkg/events.go delete mode 100644 pkg/github.go delete mode 100644 pkg/grafana.go delete mode 100644 pkg/router.go delete mode 100644 pkg/simulator.go delete mode 100644 pkg/slack.go delete mode 100755 scripts/send-stub-webhook.sh delete mode 100755 scripts/wait-for-it.sh delete mode 100755 scripts/wait.sh diff --git a/.env-example b/.env-example deleted file mode 100644 index 2adf13d..0000000 --- a/.env-example +++ /dev/null @@ -1 +0,0 @@ -SLACK_WEBHOOK_URL= diff --git a/LICENSE b/LICENSE index 20c82ae..f0fe887 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Three Dots Labs +Copyright (c) 2023 Furkan Gulsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 43c6aed..20366f6 100644 --- a/README.md +++ b/README.md @@ -1,52 +1 @@ -# Event-Driven application example - -This is an example Event-Driven application written in Go, using [Watermill](https://github.com/ThreeDotsLabs/watermill). - -The projects aims to integrate incoming GitHub webhooks with Grafana and Slack, essentially adding annotations and -sending messages when a new commit is pushed. There are also simulated deployment messages sent over RabbitMQ to -demonstrate working with multiple event streams. - -![](https://threedots.tech/media/event-driven-applications/diagram.png) - -An example result can look like this: - -![](https://threedots.tech/media/event-driven-applications/grafana.png) - -![](https://threedots.tech/media/event-driven-applications/slack.png) - -## Running - -If you'd like to integrate the example with your Slack workspace, copy `.env-example` to `.env` and fill in the -webhook URL in `SLACK_WEBHOOK_URL` variable. - -In addition to the application, the docker-compose environment consists of: - -* **Kafka** and **ZooKeeper** -* **RabbitMQ** -* **Grafana** -* **Prometheus** - -The whole environment can be run with: - -```bash -docker-compose up -``` - -You can now configure your GitHub repository to send webhooks to the application (you need to expose port `8080` to the -external network first). - -Alternatively, you can run `./scripts/send-stub-webhook.sh` to send some stub webhooks. - -Visit [localhost:3000/d/webhooks](http://localhost:3000/d/webhooks) to see annotations added in Grafana. Use -`admin:secret` as credentials. - -## Metrics - -You can access the Watermill dashboard at [localhost:3000/d/watermill](http://localhost:3000/d/watermill). See what -changes when you send more webhooks over time. - -![](https://threedots.tech/media/event-driven-applications/metrics.png) - -## What's next? - -See [Watermill's documentation](https://watermill.io/) to learn more. +# Event Drivent Architecture with Golang diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 1e0c932..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "github.com/ThreeDotsLabs/event-driven-example/pkg" - - "github.com/ThreeDotsLabs/watermill" - "github.com/ThreeDotsLabs/watermill/components/metrics" - "github.com/ThreeDotsLabs/watermill/message" - "github.com/ThreeDotsLabs/watermill/message/router/middleware" -) - -func main() { - logger := watermill.NewStdLogger(false, false) - config := pkg.LoadConfig() - - router, err := message.NewRouter(message.RouterConfig{}, logger) - if err != nil { - panic(err) - } - router.AddMiddleware(middleware.Recoverer) - - // Metrics setup - prometheusRegistry, closeMetricsServer := metrics.CreateRegistryAndServeHTTP(config.MetricsBindAddr) - defer closeMetricsServer() - metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "") - metricsBuilder.AddPrometheusRouterMetrics(router) - - err = pkg.SetupRouter(router, config, logger) - if err != nil { - panic(err) - } - - err = router.Run() - if err != nil { - panic(err) - } -} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c8e2052..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,68 +0,0 @@ -version: '3' -services: - app: - image: golang:1.11 - restart: on-failure - ports: - - 8080:8080 - volumes: - - .:/app - - $GOPATH/pkg/mod:/go/pkg/mod - working_dir: /app - command: bash -c "./scripts/wait.sh kafka:9092 rabbitmq:5672 grafana:3000 && go run ./cmd" - env_file: - - .env - - grafana: - image: grafana/grafana - ports: - - 3000:3000 - volumes: - - grafana_storage:/var/lib/grafana - - ./metrics/grafana/provisioning:/etc/grafana/provisioning/dashboards - - ./metrics/grafana/datasources:/etc/grafana/provisioning/datasources - - ./metrics/grafana/dashboards:/var/lib/grafana/dashboards - environment: - GF_SECURITY_ADMIN_PASSWORD: secret - logging: - driver: none - - zookeeper: - image: confluentinc/cp-zookeeper:latest - restart: on-failure - environment: - ZOOKEEPER_CLIENT_PORT: 2181 - logging: - driver: none - - kafka: - image: confluentinc/cp-kafka:latest - restart: on-failure - logging: - driver: none - depends_on: - - zookeeper - environment: - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" - - rabbitmq: - image: rabbitmq:3.7 - restart: on-failure - ports: - - 5672:5672 - logging: - driver: none - - prometheus: - image: prom/prometheus - restart: on-failure - volumes: - - ./metrics/prometheus.yml:/etc/prometheus/prometheus.yml - logging: - driver: none - -volumes: - grafana_storage: {} diff --git a/go.mod b/go.mod deleted file mode 100644 index 79130d9..0000000 --- a/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module github.com/ThreeDotsLabs/event-driven-example - -require ( - github.com/DataDog/zstd v1.3.5 // indirect - github.com/Shopify/toxiproxy v2.1.4+incompatible // indirect - github.com/ThreeDotsLabs/watermill v0.3.0 - github.com/go-chi/chi v4.0.1+incompatible // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 7ecfb08..0000000 --- a/go.sum +++ /dev/null @@ -1,240 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.35.1/go.mod h1:wfjPZNvXCBYESy3fIynybskMP48KVPrjSPCnXiK7Prg= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/zstd v1.3.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.3.5 h1:DtpNbljikUepEPD16hD4LvIcmhnhdLTiW/5pHgbmp14= -github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Shopify/sarama v1.20.1 h1:Bb0h3I++r4eX333Y0uZV2vwUXepJbt6ig05TUU1qt9I= -github.com/Shopify/sarama v1.20.1/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.3+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/ThreeDotsLabs/watermill v0.3.0 h1:/Dbf8kU+c5UtTJEaauLXrOnzu5Be/BvQ1MXO3838ino= -github.com/ThreeDotsLabs/watermill v0.3.0/go.mod h1:8+sP309OzQmiQRIe/mybBNKNvszj7igrIWym/1jnhaE= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-chi/chi v3.3.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/chi v4.0.1+incompatible h1:RSRC5qmFPtO90t7pTL0DBMNpZFsb/sHF3RXVlDgFisA= -github.com/go-chi/chi v4.0.1+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.1-0.20190205222052-c823c79ea157 h1:SdQMHsZ18/XZCHuwt3IF+dvHgYTO2XMWZjv3XBKQqAI= -github.com/golang/protobuf v1.2.1-0.20190205222052-c823c79ea157/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= -github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -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-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/raft v1.0.0/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/nats-io/gnatsd v1.3.0/go.mod h1:nqco77VO78hLCJpIcVfygDP2rPGfsEHkGTUk94uh5DQ= -github.com/nats-io/go-nats v1.7.0/go.mod h1:+t7RHT5ApZebkrQdnn6AhQJmhJJiKAvJUio1PiiCtj0= -github.com/nats-io/go-nats-streaming v0.4.0/go.mod h1:gfq4R3c9sKAINOpelo0gn/b9QDMBZnmrttcsNF+lqyo= -github.com/nats-io/nats-streaming-server v0.11.2/go.mod h1:RyqtDJZvMZO66YmyjIYdIvS69zu/wDAkyNWa8PIUa5c= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= -github.com/nats-io/nuid v1.0.0/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181218105931-67670fe90761 h1:z6tvbDJ5OLJ48FFmnksv04a78maSTRBUIhkdHYV5Y98= -github.com/prometheus/common v0.0.0-20181218105931-67670fe90761/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/renstrom/shortuuid v3.0.0+incompatible h1:F6T1U7bWlI3FTV+JE8HyeR7bkTeYZJntqQLA9ST4HOQ= -github.com/renstrom/shortuuid v3.0.0+incompatible/go.mod h1:n18Ycpn8DijG+h/lLBQVnGKv1BCtTeXo8KKSbBOrQ8c= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/streadway/amqp v0.0.0-20181205114330-a314942b2fd9 h1:37QTz/gdHBLQcsmgMTnQDSWCtKzJ7YnfI2M2yTdr4BQ= -github.com/streadway/amqp v0.0.0-20181205114330-a314942b2fd9/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY= -github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 h1:wR6aLKdbJ5E8m+NZWkVeT49ExjlqUe0B41zfM5/m44I= -github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.19.0/go.mod h1:AYeH0+ZxYyghG8diqaaIq/9P3VgCCt5GF2ldCY4dkFg= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190122154452-ba6ebe99b011/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -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/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -labix.org/v2/mgo v0.0.0-20140701140051-000000000287/go.mod h1:Lg7AYkt1uXJoR9oeSZ3W/8IXLdvOfIITgZnommstyz4= -launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/metrics/grafana/dashboards/watermill.json b/metrics/grafana/dashboards/watermill.json deleted file mode 100644 index 9c1af9e..0000000 --- a/metrics/grafana/dashboards/watermill.json +++ /dev/null @@ -1,1350 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "description": "Watermill is a Go library for working efficiently with message streams. It is intended for building event driven applications, enabling event sourcing, RPC over messages, sagas and basically whatever else comes to your mind. You can use conventional pub/sub implementations like Kafka or RabbitMQ, but also HTTP or MySQL binlog if that fits your use case.", - "editable": true, - "gnetId": 9777, - "graphTooltip": 0, - "id": null, - "iteration": 1550057756355, - "links": [], - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 12, - "panels": [], - "title": "Current stats", - "type": "row" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "decimals": 0, - "description": "Counts the messages received within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 0, - "y": 1 - }, - "id": 14, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": true, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "Value", - "targets": [ - { - "expr": "sum(increase(subscriber_messages_received_total{instance=~\"$node\", handler_name=~\"$handler\"}[$interval]))", - "format": "time_series", - "instant": false, - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "title": "Messages received", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "decimals": 0, - "description": "Counts the handler errors within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 3, - "y": 1 - }, - "id": 16, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(191, 27, 0, 0.33)", - "full": false, - "lineColor": "#bf1b00", - "show": true - }, - "tableColumn": "Value", - "targets": [ - { - "expr": "sum(increase(handler_execution_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\"}[$interval]))", - "format": "time_series", - "instant": false, - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "title": "Handler errors", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "0", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "decimals": 0, - "description": "Counts the messages publisher within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 6, - "y": 1 - }, - "id": 15, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(increase(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"true\"}[$interval]))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "title": "Messages published", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": "Prometheus", - "decimals": 0, - "description": "Counts the publisher errors within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 9, - "y": 1 - }, - "id": 17, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(191, 27, 0, 0.33)", - "full": false, - "lineColor": "#bf1b00", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(increase(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\"}[$interval]))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "", - "title": "Publisher errors", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "0", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#d44a3a", - "rgba(237, 129, 40, 0.89)", - "#299c46" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 12, - "y": 1 - }, - "id": 18, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(57, 232, 0, 0.5)", - "full": false, - "lineColor": "rgba(57, 232, 0, 1)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(count(subscriber_messages_received_total{acked=\"acked\"}) by (instance))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "-1, 0", - "title": "Subscriber count", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "0", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#d44a3a", - "rgba(237, 129, 40, 0.89)", - "#299c46" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 15, - "y": 1 - }, - "id": 19, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(57, 232, 0, 0.5)", - "full": false, - "lineColor": "rgba(57, 232, 0, 1)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(count(handler_execution_time_seconds_count{success=\"true\"}) by (instance))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "-1, 0", - "title": "Handler count", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "0", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#d44a3a", - "rgba(237, 129, 40, 0.89)", - "#299c46" - ], - "datasource": "Prometheus", - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 18, - "y": 1 - }, - "id": 20, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(57, 232, 0, 0.5)", - "full": false, - "lineColor": "rgba(57, 232, 0, 1)", - "show": true - }, - "tableColumn": "", - "targets": [ - { - "expr": "sum(count(publish_time_seconds_count{success=\"true\"}) by (instance))", - "format": "time_series", - "intervalFactor": 1, - "refId": "A" - } - ], - "thresholds": "-1, 0", - "title": "Publisher count", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "0", - "value": "null" - } - ], - "valueName": "current" - }, - { - "content": "
\n \n \n \n \n \n \n
", - "gridPos": { - "h": 4, - "w": 3, - "x": 21, - "y": 1 - }, - "id": 22, - "links": [], - "mode": "html", - "title": "Made by", - "type": "text" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 5 - }, - "id": 10, - "panels": [], - "title": "Handler & publisher operations", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 6 - }, - "id": 2, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(handler_execution_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by (handler_name, instance)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{handler_name}}@{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeShift": null, - "title": "Handler op/s", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transparent": false, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 6 - }, - "id": 8, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, instance, publisher_name)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{publisher_name}}@{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeShift": null, - "title": "Publish op/s", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transparent": false, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 3, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 14 - }, - "id": 5, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": true, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "repeat": null, - "repeatDirection": "h", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.99, sum(rate(handler_execution_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by (le))", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "p99", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.90, sum(rate(handler_execution_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by ( le))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p90", - "refId": "B" - }, - { - "expr": "histogram_quantile(0.50, sum(rate(handler_execution_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by (le))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p50", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeShift": null, - "title": "Handler duration @ $node nodes", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transparent": false, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "decimals": 1, - "fill": 3, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 14 - }, - "id": 6, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": true, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.99, sum(rate(publish_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, le, publisher_name))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p99 {{handler_name}}→{{publisher_name}}", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.90, sum(rate(publish_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, le, publisher_name))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p90 {{handler_name}}→{{publisher_name}}", - "refId": "B" - }, - { - "expr": "histogram_quantile(0.50, sum(rate(publish_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, le, publisher_name))", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "p50 {{handler_name}}→{{publisher_name}}", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeShift": null, - "title": "Publish duration @ $node nodes", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transparent": false, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 23 - }, - "id": 3, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(handler_execution_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\"}[$interval])) by (handler_name, instance)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{handler_name}}@{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeShift": null, - "title": "Handler failed op/s", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transparent": false, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fill": 1, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 23 - }, - "id": 7, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(irate(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, instance)", - "format": "time_series", - "intervalFactor": 1, - "legendFormat": "{{handler_name}}@{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeShift": null, - "title": "Publish failed op/s", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transparent": false, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "ops", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "5s", - "schemaVersion": 16, - "style": "dark", - "tags": [ - "go", - "golang", - "cqrs", - "kafka", - "event-driven" - ], - "templating": { - "list": [ - { - "auto": false, - "auto_count": 30, - "auto_min": "15s", - "current": { - "text": "1m", - "value": "1m" - }, - "hide": 0, - "label": null, - "name": "interval", - "options": [ - { - "selected": false, - "text": "15s", - "value": "15s" - }, - { - "selected": false, - "text": "30s", - "value": "30s" - }, - { - "selected": true, - "text": "1m", - "value": "1m" - }, - { - "selected": false, - "text": "10m", - "value": "10m" - }, - { - "selected": false, - "text": "30m", - "value": "30m" - }, - { - "selected": false, - "text": "1h", - "value": "1h" - }, - { - "selected": false, - "text": "6h", - "value": "6h" - }, - { - "selected": false, - "text": "12h", - "value": "12h" - }, - { - "selected": false, - "text": "1d", - "value": "1d" - } - ], - "query": "15s, 30s, 1m,10m,30m,1h,6h,12h,1d", - "refresh": 2, - "type": "interval" - }, - { - "allValue": "", - "current": {}, - "datasource": "Prometheus", - "hide": 0, - "includeAll": true, - "label": null, - "multi": true, - "name": "node", - "options": [], - "query": "label_values(instance)", - "refresh": 2, - "regex": "", - "sort": 5, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "hide": 0, - "includeAll": true, - "label": "handler", - "multi": true, - "name": "handler", - "options": [], - "query": "label_values(handler_name)", - "refresh": 2, - "regex": "", - "sort": 5, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Prometheus", - "hide": 0, - "includeAll": true, - "label": null, - "multi": true, - "name": "publisher", - "options": [], - "query": "label_values(publisher_name)", - "refresh": 1, - "regex": "", - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Watermill", - "uid": "watermill", - "version": 1 -} diff --git a/metrics/grafana/dashboards/webhooks.json b/metrics/grafana/dashboards/webhooks.json deleted file mode 100644 index 4c5be04..0000000 --- a/metrics/grafana/dashboards/webhooks.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - }, - { - "datasource": "-- Grafana --", - "enable": true, - "hide": false, - "iconColor": "#ef843c", - "limit": 100, - "name": "pushed", - "showIn": 0, - "tags": [ - "pushed" - ], - "type": "tags" - }, - { - "datasource": "-- Grafana --", - "enable": true, - "hide": false, - "iconColor": "#e5ac0e", - "limit": 100, - "name": "deploy-staging", - "showIn": 0, - "tags": [ - "deploy-staging" - ], - "type": "tags" - }, - { - "datasource": "-- Grafana --", - "enable": true, - "hide": false, - "iconColor": "#9ac48a", - "limit": 100, - "name": "deploy-production", - "showIn": 0, - "tags": [ - "deploy-production" - ], - "type": "tags" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 1, - "links": [], - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "fill": 1, - "gridPos": { - "h": 13, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 2, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Load", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "schemaVersion": 16, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Webhooks", - "uid": "webhooks", - "version": 4 -} diff --git a/metrics/grafana/datasources/prometheus.yml b/metrics/grafana/datasources/prometheus.yml deleted file mode 100644 index f6a3d05..0000000 --- a/metrics/grafana/datasources/prometheus.yml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: 1 -datasources: - - name: Prometheus - type: prometheus - url: http://prometheus:9090 - access: proxy diff --git a/metrics/grafana/provisioning/default.yml b/metrics/grafana/provisioning/default.yml deleted file mode 100644 index 3a6d23a..0000000 --- a/metrics/grafana/provisioning/default.yml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: 1 - -providers: -- name: 'default' - orgId: 1 - folder: '' - type: file - disableDeletion: false - options: - path: /var/lib/grafana/dashboards diff --git a/metrics/prometheus.yml b/metrics/prometheus.yml deleted file mode 100644 index 1d7ae43..0000000 --- a/metrics/prometheus.yml +++ /dev/null @@ -1,9 +0,0 @@ -global: - scrape_interval: 15s - evaluation_interval: 15s - -scrape_configs: - - job_name: 'metrics' - static_configs: - - targets: - - 'app:8081' diff --git a/pkg/amqp.go b/pkg/amqp.go deleted file mode 100644 index 41755d5..0000000 --- a/pkg/amqp.go +++ /dev/null @@ -1,42 +0,0 @@ -package pkg - -import ( - "encoding/json" - "log" - - "github.com/ThreeDotsLabs/watermill" - "github.com/ThreeDotsLabs/watermill/message" -) - -type deploymentPayload struct { - CommitID string `json:"commit_id"` - Env string `json:"env"` - Timestamp string `json:"timestamp"` -} - -// AMQPHandler receives a deployment message over AMQP and translates it into a commitDeployed event. -func AMQPHandler(msg *message.Message) ([]*message.Message, error) { - payload := deploymentPayload{} - err := json.Unmarshal(msg.Payload, &payload) - if err != nil { - return nil, err - } - - log.Println("Received AMQP message: ", payload) - - event := commitDeployed{ - ID: payload.CommitID, - Env: payload.Env, - OccurredOn: payload.Timestamp, - } - eventJSON, err := json.Marshal(event) - if err != nil { - return nil, err - } - - log.Println("Sending event:", event) - - m := message.NewMessage(watermill.NewUUID(), eventJSON) - m.Metadata.Set("event_type", "commitDeployed") - return []*message.Message{m}, nil -} diff --git a/pkg/config.go b/pkg/config.go deleted file mode 100644 index 05ce693..0000000 --- a/pkg/config.go +++ /dev/null @@ -1,62 +0,0 @@ -package pkg - -import ( - "os" - "strings" -) - -const ( - defaultBindAddr = ":8080" - defaultMetricsBindAddr = ":8081" - - defaultGrafanaURL = "http://grafana:3000" - defaultGrafanaCredentials = "admin:secret" - - defaultAMQPURI = "amqp://guest:guest@rabbitmq:5672/" - defaultAMQPQueue = "deployments" - - defaultKafkaBrokers = "kafka:9092" - defaultKafkaTopic = "events" -) - -type Config struct { - BindAddr string - MetricsBindAddr string - - AMQPURI string - AMQPQueue string - - KafkaBrokers []string - KafkaTopic string - - GrafanaURL string - GrafanaCredentials string - - SlackWebhookURL string -} - -func LoadConfig() Config { - return Config{ - MetricsBindAddr: getEnv("METRICS_BIND_ADDR", defaultMetricsBindAddr), - BindAddr: getEnv("BIND_ADDR", defaultBindAddr), - - AMQPURI: getEnv("AMQP_URI", defaultAMQPURI), - AMQPQueue: getEnv("AMQP_QUEUE", defaultAMQPQueue), - - KafkaBrokers: strings.Split(getEnv("KAFKA_BROKERS", defaultKafkaBrokers), ","), - KafkaTopic: getEnv("KAFKA_TOPIC", defaultKafkaTopic), - - GrafanaURL: getEnv("GRAFANA_URL", defaultGrafanaURL), - GrafanaCredentials: getEnv("GRAFANA_CREDENTIALS", defaultGrafanaCredentials), - - SlackWebhookURL: getEnv("SLACK_WEBHOOK_URL", ""), - } -} - -func getEnv(key string, fallback string) string { - value := os.Getenv(key) - if value != "" { - return value - } - return fallback -} diff --git a/pkg/events.go b/pkg/events.go deleted file mode 100644 index 6180acd..0000000 --- a/pkg/events.go +++ /dev/null @@ -1,14 +0,0 @@ -package pkg - -type commitPushed struct { - ID string `json:"id"` - Message string `json:"message"` - Author string `json:"author"` - OccurredOn string `json:"occurred_on"` -} - -type commitDeployed struct { - ID string `json:"id"` - Env string `json:"env"` - OccurredOn string `json:"occurred_on"` -} diff --git a/pkg/github.go b/pkg/github.go deleted file mode 100644 index b85f6c4..0000000 --- a/pkg/github.go +++ /dev/null @@ -1,55 +0,0 @@ -package pkg - -import ( - "encoding/json" - "log" - - "github.com/ThreeDotsLabs/watermill" - "github.com/ThreeDotsLabs/watermill/message" -) - -// githubPushEvent represents a part of GitHub PushEvent webhook. -// See: https://developer.github.com/v3/activity/events/types/#pushevent -type githubPushEvent struct { - Commits []struct { - ID string `json:"id"` - Message string `json:"message"` - Timestamp string `json:"timestamp"` - Author struct { - Name string `json:"name"` - } `json:"author"` - } `json:"commits"` -} - -// GithubWebhookHandler receives GitHub webhooks and translates each commit details into a commitPushed event. -func GithubWebhookHandler(msg *message.Message) ([]*message.Message, error) { - pushEvent := githubPushEvent{} - err := json.Unmarshal(msg.Payload, &pushEvent) - if err != nil { - return nil, err - } - - log.Println("Received GitHub Webhook:", pushEvent) - - var messages []*message.Message - for _, commit := range pushEvent.Commits { - event := commitPushed{ - ID: commit.ID, - Message: commit.Message, - Author: commit.Author.Name, - OccurredOn: commit.Timestamp, - } - eventJSON, err := json.Marshal(event) - if err != nil { - return nil, err - } - - log.Println("Sending push event", event) - - m := message.NewMessage(watermill.NewUUID(), eventJSON) - m.Metadata.Set("event_type", "commitPushed") - messages = append(messages, m) - } - - return messages, nil -} diff --git a/pkg/grafana.go b/pkg/grafana.go deleted file mode 100644 index cb742e8..0000000 --- a/pkg/grafana.go +++ /dev/null @@ -1,109 +0,0 @@ -package pkg - -import ( - "bytes" - "encoding/json" - "fmt" - "log" - stdHttp "net/http" - "strings" - "time" - - "github.com/ThreeDotsLabs/watermill" - "github.com/ThreeDotsLabs/watermill/message" - "github.com/ThreeDotsLabs/watermill/message/infrastructure/http" -) - -// See: http://docs.grafana.org/http_api/annotations/#create-annotation -type grafanaAnnotationsPayload struct { - Text string `json:"text"` - Tags []string `json:"tags"` - Time int64 `json:"time"` - TimeEnd int64 `json:"timeEnd"` -} - -type grafanaParams struct { - OccurredOn string - Text string - Tags []string -} - -func GrafanaMarshaller(credentials string) http.MarshalMessageFunc { - return func(url string, msg *message.Message) (*stdHttp.Request, error) { - req, err := stdHttp.NewRequest(stdHttp.MethodPost, url, bytes.NewBuffer(msg.Payload)) - if err != nil { - return nil, err - } - - c := strings.Split(credentials, ":") - req.SetBasicAuth(c[0], c[1]) - - req.Header.Set("Content-Type", "application/json") - - return req, nil - } -} - -// GrafanaHandler receives an event and translates it into an annotation payload compatible with Grafana REST API. -func GrafanaHandler(msg *message.Message) ([]*message.Message, error) { - eventType := msg.Metadata.Get("event_type") - params, err := grafanaParamsByType(eventType, msg.Payload) - if err != nil { - return nil, err - } - - parsedTime, err := time.Parse(time.RFC3339, params.OccurredOn) - if err != nil { - return nil, err - } - - timestamp := parsedTime.Unix() * 1000 - - payload := grafanaAnnotationsPayload{ - Text: params.Text, - Tags: params.Tags, - Time: timestamp, - TimeEnd: timestamp, - } - - payloadJSON, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - log.Println("Sending Grafana webhook:", string(payloadJSON)) - - m := message.NewMessage(watermill.NewUUID(), payloadJSON) - return []*message.Message{m}, nil -} - -func grafanaParamsByType(eventType string, payload []byte) (grafanaParams, error) { - switch eventType { - case "commitPushed": - event := commitPushed{} - err := json.Unmarshal(payload, &event) - if err != nil { - return grafanaParams{}, err - } - - return grafanaParams{ - OccurredOn: event.OccurredOn, - Text: fmt.Sprintf("Commit %s pushed by %s", event.ID, event.Author), - Tags: []string{"pushed"}, - }, nil - case "commitDeployed": - event := commitDeployed{} - err := json.Unmarshal(payload, &event) - if err != nil { - return grafanaParams{}, err - } - - return grafanaParams{ - OccurredOn: event.OccurredOn, - Text: fmt.Sprintf("Commit %s deployed to %s", event.ID, event.Env), - Tags: []string{fmt.Sprintf("deploy-%s", event.Env)}, - }, nil - default: - return grafanaParams{}, fmt.Errorf("unknown event type: %s", eventType) - } -} diff --git a/pkg/router.go b/pkg/router.go deleted file mode 100644 index 5eb9a64..0000000 --- a/pkg/router.go +++ /dev/null @@ -1,141 +0,0 @@ -package pkg - -import ( - "math/rand" - "time" - - "github.com/ThreeDotsLabs/watermill" - "github.com/ThreeDotsLabs/watermill/message" - "github.com/ThreeDotsLabs/watermill/message/infrastructure/amqp" - "github.com/ThreeDotsLabs/watermill/message/infrastructure/http" - "github.com/ThreeDotsLabs/watermill/message/infrastructure/kafka" -) - -func SetupRouter(router *message.Router, c Config, logger watermill.LoggerAdapter) error { - amqpPublisher, err := amqp.NewPublisher(amqp.NewDurableQueueConfig(c.AMQPURI), logger) - if err != nil { - return err - } - - amqpConfig := amqp.NewDurableQueueConfig(c.AMQPURI) - amqpSubscriber, err := amqp.NewSubscriber(amqpConfig, logger) - if err != nil { - return err - } - - httpConfig := http.SubscriberConfig{ - UnmarshalMessageFunc: http.DefaultUnmarshalMessageFunc, - } - httpSubscriber, err := http.NewSubscriber(c.BindAddr, httpConfig, logger) - if err != nil { - return err - } - - kafkaPublisher, err := kafka.NewPublisher( - c.KafkaBrokers, - kafka.DefaultMarshaler{}, - nil, - logger) - if err != nil { - return err - } - - kafkaConfig := kafka.SubscriberConfig{ - Brokers: c.KafkaBrokers, - } - kafkaSubscriber, err := kafka.NewSubscriber( - kafkaConfig, - nil, - kafka.DefaultMarshaler{}, - logger) - if err != nil { - return err - } - - grafanaPublisher, err := http.NewPublisher( - http.PublisherConfig{ - MarshalMessageFunc: GrafanaMarshaller(c.GrafanaCredentials), - }, logger) - if err != nil { - return err - } - - router.AddHandler( - "http-to-kafka", - "/", - httpSubscriber, - c.KafkaTopic, - kafkaPublisher, - GithubWebhookHandler, - ) - - router.AddHandler( - "rabbitmq-to-kafka", - c.AMQPQueue, - amqpSubscriber, - c.KafkaTopic, - kafkaPublisher, - AMQPHandler, - ) - - router.AddHandler( - "kafka-to-grafana", - c.KafkaTopic, - kafkaSubscriber, - c.GrafanaURL+"/api/annotations", - grafanaPublisher, - GrafanaHandler, - ) - - if c.SlackWebhookURL != "" { - slackPublisher, err := http.NewPublisher( - http.PublisherConfig{ - MarshalMessageFunc: SlackMarshaller, - }, logger) - if err != nil { - return err - } - - router.AddHandler( - "kafka-to-slack", - c.KafkaTopic, - kafkaSubscriber, - c.SlackWebhookURL, - slackPublisher, - SlackHandler, - ) - } - - // Simulate commit deploys with delays - stagingDelay := time.Second * time.Duration(rand.Intn(60)+30) - productionDelay := stagingDelay + time.Second*time.Duration(rand.Intn(120)+60) - - router.AddHandler( - "deploy-staging-simulator", - c.KafkaTopic, - kafkaSubscriber, - c.AMQPQueue, - amqpPublisher, - DeploySimulator{"staging", stagingDelay}.Handle, - ) - - router.AddHandler( - "deploy-production-simulator", - c.KafkaTopic, - kafkaSubscriber, - c.AMQPQueue, - amqpPublisher, - DeploySimulator{"production", productionDelay}.Handle, - ) - - go func() { - // Start HTTP server only after the router is running - <-router.Running() - err = httpSubscriber.StartHTTPServer() - if err != nil { - panic(err) - } - }() - - return nil -} \ No newline at end of file diff --git a/pkg/simulator.go b/pkg/simulator.go deleted file mode 100644 index 799085f..0000000 --- a/pkg/simulator.go +++ /dev/null @@ -1,54 +0,0 @@ -package pkg - -import ( - "encoding/json" - "log" - "time" - - "github.com/ThreeDotsLabs/watermill" - "github.com/ThreeDotsLabs/watermill/message" -) - -// DeploySimulator simulates deployment messages for each commitPushed event. -type DeploySimulator struct { - Env string - Delay time.Duration -} - -// Handle receives a commitPushed event, waits for chosen duration and produces a commitDeployed event. -func (d DeploySimulator) Handle(msg *message.Message) ([]*message.Message, error) { - if msg.Metadata.Get("event_type") != "commitPushed" { - return nil, nil - } - - event := commitPushed{} - err := json.Unmarshal(msg.Payload, &event) - if err != nil { - return nil, err - } - - baseTime, err := time.Parse(time.RFC3339, event.OccurredOn) - if err != nil { - return nil, err - } - - log.Println("Received new event:", event) - - time.Sleep(d.Delay) - - payload := deploymentPayload{ - CommitID: event.ID, - Env: d.Env, - Timestamp: baseTime.Add(d.Delay).Format(time.RFC3339), - } - - log.Println("Sending event:", payload) - - payloadJSON, err := json.Marshal(payload) - if err != nil { - panic(err) - } - - m := message.NewMessage(watermill.NewUUID(), payloadJSON) - return []*message.Message{m}, nil -} diff --git a/pkg/slack.go b/pkg/slack.go deleted file mode 100644 index dd95b61..0000000 --- a/pkg/slack.go +++ /dev/null @@ -1,74 +0,0 @@ -package pkg - -import ( - "bytes" - "encoding/json" - "fmt" - "log" - "net/http" - - "github.com/ThreeDotsLabs/watermill" - "github.com/ThreeDotsLabs/watermill/message" -) - -// See: https://api.slack.com/incoming-webhooks -type slackMessagePayload struct { - Text string `json:"text"` -} - -func SlackMarshaller(url string, msg *message.Message) (*http.Request, error) { - req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(msg.Payload)) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - - return req, nil -} - -// SlackHandler receives an event and translates it into a message payload compatible with Slack REST API. -func SlackHandler(msg *message.Message) ([]*message.Message, error) { - eventType := msg.Metadata.Get("event_type") - text, err := slackTextByType(eventType, msg.Payload) - - payload := slackMessagePayload{ - Text: text, - } - - payloadJSON, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - log.Println("Sending Slack webhook:", string(payloadJSON)) - - m := message.NewMessage(watermill.NewUUID(), payloadJSON) - return []*message.Message{m}, nil -} - -func slackTextByType(eventType string, payload []byte) (string, error) { - switch eventType { - case "commitPushed": - event := commitPushed{} - err := json.Unmarshal(payload, &event) - if err != nil { - return "", err - } - - text := fmt.Sprintf(":rocket: Commit `%s` *pushed* by %s: _%s_", - event.ID, event.Author, event.Message) - return text, nil - case "commitDeployed": - event := commitDeployed{} - err := json.Unmarshal(payload, &event) - if err != nil { - return "", err - } - - text := fmt.Sprintf(":heavy_check_mark: Commit `%s` deployed to *%s*", event.ID, event.Env) - return text, nil - default: - return "", fmt.Errorf("unknown event type: %s", eventType) - } -} diff --git a/scripts/send-stub-webhook.sh b/scripts/send-stub-webhook.sh deleted file mode 100755 index 035bca9..0000000 --- a/scripts/send-stub-webhook.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -readonly payload="$(dirname "$0")/payload.json" -readonly time=$(date --rfc-3339=seconds | tr ' ' 'T') -readonly id=$(tr -dc 'a-f0-9' < /dev/urandom | head -c40) - -cat >"$payload" <&2; fi } - -usage() -{ - cat << USAGE >&2 -Usage: - $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] - -h HOST | --host=HOST Host or IP under test - -p PORT | --port=PORT TCP port under test - Alternatively, you specify the host and port as host:port - -s | --strict Only execute subcommand if the test succeeds - -q | --quiet Don't output any status messages - -t TIMEOUT | --timeout=TIMEOUT - Timeout in seconds, zero for no timeout - -- COMMAND ARGS Execute command with args after the test finishes -USAGE - exit 1 -} - -wait_for() -{ - if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then - echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" - else - echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" - fi - WAITFORIT_start_ts=$(date +%s) - while : - do - if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then - nc -z $WAITFORIT_HOST $WAITFORIT_PORT - WAITFORIT_result=$? - else - (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 - WAITFORIT_result=$? - fi - if [[ $WAITFORIT_result -eq 0 ]]; then - WAITFORIT_end_ts=$(date +%s) - echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" - break - fi - sleep 1 - done - return $WAITFORIT_result -} - -wait_for_wrapper() -{ - # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 - if [[ $WAITFORIT_QUIET -eq 1 ]]; then - timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & - else - timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & - fi - WAITFORIT_PID=$! - trap "kill -INT -$WAITFORIT_PID" INT - wait $WAITFORIT_PID - WAITFORIT_RESULT=$? - if [[ $WAITFORIT_RESULT -ne 0 ]]; then - echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" - fi - return $WAITFORIT_RESULT -} - -# process arguments -while [[ $# -gt 0 ]] -do - case "$1" in - *:* ) - WAITFORIT_hostport=(${1//:/ }) - WAITFORIT_HOST=${WAITFORIT_hostport[0]} - WAITFORIT_PORT=${WAITFORIT_hostport[1]} - shift 1 - ;; - --child) - WAITFORIT_CHILD=1 - shift 1 - ;; - -q | --quiet) - WAITFORIT_QUIET=1 - shift 1 - ;; - -s | --strict) - WAITFORIT_STRICT=1 - shift 1 - ;; - -h) - WAITFORIT_HOST="$2" - if [[ $WAITFORIT_HOST == "" ]]; then break; fi - shift 2 - ;; - --host=*) - WAITFORIT_HOST="${1#*=}" - shift 1 - ;; - -p) - WAITFORIT_PORT="$2" - if [[ $WAITFORIT_PORT == "" ]]; then break; fi - shift 2 - ;; - --port=*) - WAITFORIT_PORT="${1#*=}" - shift 1 - ;; - -t) - WAITFORIT_TIMEOUT="$2" - if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi - shift 2 - ;; - --timeout=*) - WAITFORIT_TIMEOUT="${1#*=}" - shift 1 - ;; - --) - shift - WAITFORIT_CLI=("$@") - break - ;; - --help) - usage - ;; - *) - echoerr "Unknown argument: $1" - usage - ;; - esac -done - -if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then - echoerr "Error: you need to provide a host and port to test." - usage -fi - -WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} -WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} -WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} -WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} - -# check to see if timeout is from busybox? -WAITFORIT_TIMEOUT_PATH=$(type -p timeout) -WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) -if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then - WAITFORIT_ISBUSY=1 - WAITFORIT_BUSYTIMEFLAG="-t" - -else - WAITFORIT_ISBUSY=0 - WAITFORIT_BUSYTIMEFLAG="" -fi - -if [[ $WAITFORIT_CHILD -gt 0 ]]; then - wait_for - WAITFORIT_RESULT=$? - exit $WAITFORIT_RESULT -else - if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then - wait_for_wrapper - WAITFORIT_RESULT=$? - else - wait_for - WAITFORIT_RESULT=$? - fi -fi - -if [[ $WAITFORIT_CLI != "" ]]; then - if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then - echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" - exit $WAITFORIT_RESULT - fi - exec "${WAITFORIT_CLI[@]}" -else - exit $WAITFORIT_RESULT -fi diff --git a/scripts/wait.sh b/scripts/wait.sh deleted file mode 100755 index 7369ac2..0000000 --- a/scripts/wait.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -for service in "$@"; do - "$(dirname "$0")/wait-for-it.sh" -t 30 "$service" -done From 1e980d4502b19ea5c15f02693d534b5172921fa4 Mon Sep 17 00:00:00 2001 From: Furkan-Gulsen Date: Sat, 18 Nov 2023 22:15:23 +0300 Subject: [PATCH 2/7] feat: Add initial code for EDA with Golang --- cmd/app/main.go | 30 +++++++++++++++++++++++++ config/config.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 26 ++++++++++++++++++++++ go.sum | 54 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 cmd/app/main.go create mode 100644 config/config.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/cmd/app/main.go b/cmd/app/main.go new file mode 100644 index 0000000..3aa9991 --- /dev/null +++ b/cmd/app/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/config" + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill/components/metrics" + "github.com/ThreeDotsLabs/watermill/message" + "github.com/ThreeDotsLabs/watermill/message/router/middleware" +) + +func main() { + + // Create a new logger. + logger := watermill.NewStdLogger(false, false) + c := config.LoadConfig() + + // Create a new router. + router, err := message.NewRouter(message.RouterConfig{}, logger) + if err != nil { + panic(err) + } + router.AddMiddleware(middleware.Recoverer) + + // Create a new Prometheus registry and serve it on the given address. + prometheusRegistry, closeMetricsServer := metrics.CreateRegistryAndServeHTTP(c.MetricsBindAddr) + defer closeMetricsServer() + metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "") + metricsBuilder.AddPrometheusRouterMetrics(router) + +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..938b69b --- /dev/null +++ b/config/config.go @@ -0,0 +1,58 @@ +package config + +import ( + "os" + "strings" +) + +const ( + defaultBindAddr = ":8080" + defaultMetricsBindAddr = ":8081" + + defaultGrafanaURL = "http://grafana:3000" + defaultGrafanaCredentials = "admin:secret" + + defaultAMQPURI = "amqp://guest:guest@rabbitmq:5672/" + defaultAMQPQueue = "deployments" + + defaultKafkaBrokers = "kafka:9092" + defaultKafkaTopic = "events" +) + +type Config struct { + BindAddr string + MetricsBindAddr string + + GrafanaURL string + GrafanaCredentials string + + AMQPURI string + AMQPQueue string + + KafkaBrokers []string + KafkaTopic string +} + +func LoadConfig() Config { + return Config{ + MetricsBindAddr: getEnv("METRICS_BIND_ADDR", defaultMetricsBindAddr), + BindAddr: getEnv("BIND_ADDR", defaultBindAddr), + + AMQPURI: getEnv("AMQP_URI", defaultAMQPURI), + AMQPQueue: getEnv("AMQP_QUEUE", defaultAMQPQueue), + + KafkaBrokers: strings.Split(getEnv("KAFKA_BROKERS", defaultKafkaBrokers), ","), + KafkaTopic: getEnv("KAFKA_TOPIC", defaultKafkaTopic), + + GrafanaURL: getEnv("GRAFANA_URL", defaultGrafanaURL), + GrafanaCredentials: getEnv("GRAFANA_CREDENTIALS", defaultGrafanaCredentials), + } +} + +func getEnv(key string, fallback string) string { + value := os.Getenv(key) + if value != "" { + return value + } + return fallback +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1834a56 --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang + +go 1.21.3 + +require ( + github.com/ThreeDotsLabs/watermill v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v3 v3.2.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/go-chi/chi/v5 v5.0.8 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/lithammer/shortuuid/v3 v3.0.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.39.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/sony/gobreaker v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ffc4fff --- /dev/null +++ b/go.sum @@ -0,0 +1,54 @@ +github.com/ThreeDotsLabs/watermill v1.3.5 h1:50JEPEhMGZQMh08ct0tfO1PsgMOAOhV3zxK2WofkbXg= +github.com/ThreeDotsLabs/watermill v1.3.5/go.mod h1:O/u/Ptyrk5MPTxSeWM5vzTtZcZfxXfO9PK9eXTYiFZY= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= +github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8= +github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= From daeb0ec84fdf97d203b49d6a344b562764991867 Mon Sep 17 00:00:00 2001 From: Furkan-Gulsen Date: Sat, 18 Nov 2023 22:18:28 +0300 Subject: [PATCH 3/7] feat: Add context and setup router in main --- cmd/app/main.go | 22 ++++++ go.mod | 33 +++++++- go.sum | 186 +++++++++++++++++++++++++++++++++++++++++++++ internal/router.go | 103 +++++++++++++++++++++++++ 4 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 internal/router.go diff --git a/cmd/app/main.go b/cmd/app/main.go index 3aa9991..c94a6fe 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -1,7 +1,10 @@ package main import ( + "context" + "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/config" + "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/internal" "github.com/ThreeDotsLabs/watermill" "github.com/ThreeDotsLabs/watermill/components/metrics" "github.com/ThreeDotsLabs/watermill/message" @@ -9,6 +12,9 @@ import ( ) func main() { + // Create a new context and add a cancel function to it. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // Create a new logger. logger := watermill.NewStdLogger(false, false) @@ -27,4 +33,20 @@ func main() { metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "") metricsBuilder.AddPrometheusRouterMetrics(router) + // Setup the router. + r := &internal.Router{ + Router: router, + Logger: logger, + Config: c, + } + err = r.SetupRouter() + if err != nil { + panic(err) + } + + // Run the router. + if err := router.Run(ctx); err != nil { + panic(err) + } + } diff --git a/go.mod b/go.mod index 1834a56..dea890a 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,55 @@ module github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang go 1.21.3 require ( - github.com/ThreeDotsLabs/watermill v1.3.5 // indirect + github.com/ThreeDotsLabs/watermill v1.3.5 + github.com/ThreeDotsLabs/watermill-amqp v1.1.4 + github.com/ThreeDotsLabs/watermill-http v1.1.4 + github.com/ThreeDotsLabs/watermill-kafka/v2 v2.5.0 +) + +require ( + github.com/Shopify/sarama v1.38.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/eapache/go-resiliency v1.3.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect + github.com/eapache/queue v1.1.0 // indirect + github.com/go-chi/chi v4.0.2+incompatible // indirect github.com/go-chi/chi/v5 v5.0.8 // indirect + github.com/go-chi/render v1.0.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.3.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/klauspost/compress v1.15.11 // indirect github.com/lithammer/shortuuid/v3 v3.0.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/sony/gobreaker v0.5.0 // indirect + github.com/streadway/amqp v1.0.0 // indirect + go.opentelemetry.io/contrib/instrumentation/github.com/Shopify/sarama/otelsarama v0.31.0 // indirect + go.opentelemetry.io/otel v1.6.1 // indirect + go.opentelemetry.io/otel/trace v1.6.1 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/net v0.4.0 // indirect golang.org/x/sys v0.4.0 // indirect google.golang.org/protobuf v1.28.1 // indirect ) diff --git a/go.sum b/go.sum index ffc4fff..1616124 100644 --- a/go.sum +++ b/go.sum @@ -1,54 +1,240 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Shopify/sarama v1.32.0/go.mod h1:+EmJJKZWVT/faR9RcOxJerP+LId4iWdQPBGLy1Y1Njs= +github.com/Shopify/sarama v1.38.0 h1:Q81EWxDT2Xs7kCaaiDGV30GyNCWd6K1Xmd4k2qpTWE8= +github.com/Shopify/sarama v1.38.0/go.mod h1:djdek3V4gS0N9LZ+OhfuuM6rE1bEKeDffYY8UvsRNyM= +github.com/Shopify/toxiproxy/v2 v2.3.0/go.mod h1:KvQTtB6RjCJY4zqNJn7C7JDFgsG5uoHYDirfUfpIm0c= +github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc= +github.com/Shopify/toxiproxy/v2 v2.5.0/go.mod h1:yhM2epWtAmel9CB8r2+L+PCmhH6yH2pITaPAo7jxJl0= +github.com/ThreeDotsLabs/watermill v1.1.0/go.mod h1:Qd1xNFxolCAHCzcMrm6RnjW0manbvN+DJVWc1MWRFlI= github.com/ThreeDotsLabs/watermill v1.3.5 h1:50JEPEhMGZQMh08ct0tfO1PsgMOAOhV3zxK2WofkbXg= github.com/ThreeDotsLabs/watermill v1.3.5/go.mod h1:O/u/Ptyrk5MPTxSeWM5vzTtZcZfxXfO9PK9eXTYiFZY= +github.com/ThreeDotsLabs/watermill-amqp v1.1.4 h1:vOdc8a0m0sMPAJZ2CMLx5a+fwlgeeojOFPwgj7+nlJA= +github.com/ThreeDotsLabs/watermill-amqp v1.1.4/go.mod h1:5RtpKNTriXCWQZ67YDg1G7qsphZoUue/EWOmQqTZi3Q= +github.com/ThreeDotsLabs/watermill-http v1.1.4 h1:wRM54z/BPnIWjGbXMrOnwOlrCAESzoSNxTAHiLysFA4= +github.com/ThreeDotsLabs/watermill-http v1.1.4/go.mod h1:mkQ9CC0pxTZerNwr281rBoOy355vYt/lePkmYSX/BRg= +github.com/ThreeDotsLabs/watermill-kafka/v2 v2.5.0 h1:/KYEjLlLx6nW3jn6AEcwAlWkPWP62zi/sUsEP4uKkZE= +github.com/ThreeDotsLabs/watermill-kafka/v2 v2.5.0/go.mod h1:w+9jhI7x5ZP67ceSUIIpkgLzjAakotfHX4sWyqsKVjs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.3.0 h1:RRL0nge+cWGlxXbUzJ7yMcq6w2XBEr19dCN6HECGaT0= +github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= +github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= +github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= +github.com/jcmturner/gokrb5/v8 v8.4.3 h1:iTonLeSJOn7MVUtyMT+arAn5AKAPrkilzhGw8wE/Tq8= +github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lithammer/shortuuid/v3 v3.0.4/go.mod h1:RviRjexKqIzx/7r1peoAITm6m7gnif/h+0zmolKJjzw= github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8= github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= +github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.0/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +go.opentelemetry.io/contrib/instrumentation/github.com/Shopify/sarama/otelsarama v0.31.0 h1:J8jI81RCB7U9a3qsTZXM/38XrvbLJCye6J32bfQctYY= +go.opentelemetry.io/contrib/instrumentation/github.com/Shopify/sarama/otelsarama v0.31.0/go.mod h1:72+cPzsW6geApbceSLMbZtYZeGMgtRDw5TcSEsdGlhc= +go.opentelemetry.io/otel v1.6.1 h1:6r1YrcTenBvYa1x491d0GGpTVBsNECmrc/K6b+zDeis= +go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ= +go.opentelemetry.io/otel/trace v1.6.1 h1:f8c93l5tboBYZna1nWk0W9DYyMzJXDWdZcJZ0Kb400U= +go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/router.go b/internal/router.go new file mode 100644 index 0000000..86283e6 --- /dev/null +++ b/internal/router.go @@ -0,0 +1,103 @@ +package internal + +import ( + "fmt" + + "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/config" + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill-amqp/pkg/amqp" + "github.com/ThreeDotsLabs/watermill-http/pkg/http" + "github.com/ThreeDotsLabs/watermill-kafka/v2/pkg/kafka" + "github.com/ThreeDotsLabs/watermill/message" +) + +type Router struct { + Router *message.Router + Logger watermill.LoggerAdapter + Config config.Config +} + +// * SetupAmqp creates a new AMQP publisher and subscriber. +func (r *Router) SetupAmqp() (message.Publisher, message.Subscriber, error) { + amqpConfig := amqp.NewDurableQueueConfig(r.Config.AMQPURI) + + amqpPublisher, err := amqp.NewPublisher(amqpConfig, r.Logger) + if err != nil { + return nil, nil, fmt.Errorf("failed to create AMQP publisher: %w", err) + } + + amqpSubscriber, err := amqp.NewSubscriber(amqpConfig, r.Logger) + if err != nil { + return nil, nil, fmt.Errorf("failed to create AMQP subscriber: %w", err) + } + + return amqpPublisher, amqpSubscriber, nil +} + +// * SetupHttpSubscriber creates a new HTTP subscriber. +func (r *Router) SetupHttpSubscriber() (message.Subscriber, error) { + httpConfig := http.SubscriberConfig{ + UnmarshalMessageFunc: http.DefaultUnmarshalMessageFunc, + } + httpSubscriber, err := http.NewSubscriber(r.Config.BindAddr, httpConfig, r.Logger) + if err != nil { + return nil, fmt.Errorf("failed to create HTTP subscriber: %w", err) + } + + return httpSubscriber, nil +} + +// * SetupKafka creates a new Kafka publisher and subscriber. +func (r *Router) SetupKafka() (message.Publisher, message.Subscriber, error) { + kafkaPublisher, err := kafka.NewPublisher( + kafka.PublisherConfig{ + Brokers: r.Config.KafkaBrokers, + Marshaler: kafka.DefaultMarshaler{}, + }, + r.Logger, + ) + if err != nil { + return nil, nil, fmt.Errorf("failed to create Kafka publisher: %w", err) + } + + kafkaSubscriber, err := kafka.NewSubscriber( + kafka.SubscriberConfig{ + Brokers: r.Config.KafkaBrokers, + Unmarshaler: kafka.DefaultMarshaler{}, + OverwriteSaramaConfig: nil, + ConsumerGroup: "events", + }, + r.Logger, + ) + if err != nil { + return nil, nil, fmt.Errorf("failed to create Kafka subscriber: %w", err) + } + + return kafkaPublisher, kafkaSubscriber, nil +} + +// * SetupGrafana creates a new Grafana publisher and subscriber. +func (r *Router) SetupGrafana() error { + return nil +} + +func (r *Router) SetupRouter() error { + // amqpPublisher, amqpSubscriber, err := r.SetupAmqp() + // if err != nil { + // return err + // } + + // httpSubscriber, err := r.SetupHttpSubscriber() + // if err != nil { + // return err + // } + + // kafkaPublisher, kafkaSubscriber, err := r.SetupKafka() + // if err != nil { + // return err + // } + + // grafanaPublisher, err := r.SetupGrafana() + + return nil +} From 90f15bd1acd7c12f35bc3f9a1972d6fd4d667999 Mon Sep 17 00:00:00 2001 From: Furkan-Gulsen Date: Sat, 18 Nov 2023 22:18:47 +0300 Subject: [PATCH 4/7] feat: setup grafana --- pkg/events.go | 14 +++++++ pkg/grafana.go | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 pkg/events.go create mode 100644 pkg/grafana.go diff --git a/pkg/events.go b/pkg/events.go new file mode 100644 index 0000000..19b4907 --- /dev/null +++ b/pkg/events.go @@ -0,0 +1,14 @@ +package pkg + +type CommitPushed struct { + ID string `json:"id"` + Message string `json:"message"` + Author string `json:"author"` + OccurredOn string `json:"occurred_on"` +} + +type CommitDeployed struct { + ID string `json:"id"` + Env string `json:"env"` + OccurredOn string `json:"occurred_on"` +} diff --git a/pkg/grafana.go b/pkg/grafana.go new file mode 100644 index 0000000..a773ed2 --- /dev/null +++ b/pkg/grafana.go @@ -0,0 +1,110 @@ +package pkg + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + stdHttp "net/http" + "strings" + "time" + + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill-http/pkg/http" + "github.com/ThreeDotsLabs/watermill/message" +) + +type grafanaAnnotationsPayload struct { + Text string `json:"text"` + Tags []string `json:"tags"` + Time int64 `json:"time"` + TimeEnd int64 `json:"timeEnd"` +} + +type grafanaParams struct { + OccurredOn string + Text string + Tags []string +} + +// * GrafanaMarshaller creates a new HTTP marshaller for Grafana +func GrafanaMarshaller(credentials string) http.MarshalMessageFunc { + return func(url string, msg *message.Message) (*stdHttp.Request, error) { + req, err := stdHttp.NewRequest(stdHttp.MethodPost, url, bytes.NewBuffer(msg.Payload)) + if err != nil { + return nil, err + } + + c := strings.Split(credentials, ":") + req.SetBasicAuth(c[0], c[1]) + + req.Header.Set("Content-Type", "application/json") + + return req, nil + } +} + +// * GrafanaHandler creates a new handler for Grafana +func GrafanaHandler(msg *message.Message) ([]*message.Message, error) { + eventType := msg.Metadata.Get("event_type") + params, err := grafanaParamsByType(eventType, msg.Payload) + if err != nil { + return nil, err + } + + parsedTime, err := time.Parse(time.RFC3339, params.OccurredOn) + if err != nil { + return nil, err + } + + timestamp := parsedTime.Unix() * 1000 + + payload := grafanaAnnotationsPayload{ + Text: params.Text, + Tags: params.Tags, + Time: timestamp, + TimeEnd: timestamp, + } + + payloadJSON, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + log.Println("Sending Grafana webhook:", string(payloadJSON)) + + m := message.NewMessage(watermill.NewUUID(), payloadJSON) + return []*message.Message{m}, nil +} + +// * grafanaParamsByType returns Grafana parameters by event type +func grafanaParamsByType(eventType string, payload []byte) (grafanaParams, error) { + switch eventType { + case "commitPushed": + event := CommitPushed{} + err := json.Unmarshal(payload, &event) + if err != nil { + return grafanaParams{}, err + } + + return grafanaParams{ + OccurredOn: event.OccurredOn, + Text: fmt.Sprintf("Commit %s pushed by %s", event.ID, event.Author), + Tags: []string{"pushed"}, + }, nil + case "commitDeployed": + event := CommitDeployed{} + err := json.Unmarshal(payload, &event) + if err != nil { + return grafanaParams{}, err + } + + return grafanaParams{ + OccurredOn: event.OccurredOn, + Text: fmt.Sprintf("Commit %s deployed to %s", event.ID, event.Env), + Tags: []string{fmt.Sprintf("deploy-%s", event.Env)}, + }, nil + default: + return grafanaParams{}, fmt.Errorf("unknown event type: %s", eventType) + } +} From 4521b4307a602c93d93c8fa7bd87d73dc0686131 Mon Sep 17 00:00:00 2001 From: Furkan-Gulsen Date: Sat, 18 Nov 2023 22:59:30 +0300 Subject: [PATCH 5/7] feat: completed router and pkg --- internal/router.go | 111 +++++++++++++++++++++++++++++++++++++-------- pkg/amqp.go | 41 +++++++++++++++++ pkg/events.go | 2 + pkg/github.go | 54 ++++++++++++++++++++++ pkg/simulator.go | 54 ++++++++++++++++++++++ 5 files changed, 244 insertions(+), 18 deletions(-) create mode 100644 pkg/amqp.go create mode 100644 pkg/github.go create mode 100644 pkg/simulator.go diff --git a/internal/router.go b/internal/router.go index 86283e6..5d300e1 100644 --- a/internal/router.go +++ b/internal/router.go @@ -2,8 +2,11 @@ package internal import ( "fmt" + "math/rand" + "time" "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/config" + "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/pkg" "github.com/ThreeDotsLabs/watermill" "github.com/ThreeDotsLabs/watermill-amqp/pkg/amqp" "github.com/ThreeDotsLabs/watermill-http/pkg/http" @@ -77,27 +80,99 @@ func (r *Router) SetupKafka() (message.Publisher, message.Subscriber, error) { } // * SetupGrafana creates a new Grafana publisher and subscriber. -func (r *Router) SetupGrafana() error { - return nil +func (r *Router) SetupGrafana() (message.Publisher, error) { + grafanaPublisher, err := http.NewPublisher( + http.PublisherConfig{ + MarshalMessageFunc: pkg.GrafanaMarshaller(r.Config.GrafanaCredentials), + }, r.Logger) + if err != nil { + return nil, fmt.Errorf("failed to create Grafana publisher: %w", err) + } + + return grafanaPublisher, nil } func (r *Router) SetupRouter() error { - // amqpPublisher, amqpSubscriber, err := r.SetupAmqp() - // if err != nil { - // return err - // } - - // httpSubscriber, err := r.SetupHttpSubscriber() - // if err != nil { - // return err - // } - - // kafkaPublisher, kafkaSubscriber, err := r.SetupKafka() - // if err != nil { - // return err - // } - - // grafanaPublisher, err := r.SetupGrafana() + amqpPublisher, amqpSubscriber, err := r.SetupAmqp() + if err != nil { + return err + } + + httpSubscriber, err := r.SetupHttpSubscriber() + if err != nil { + return err + } + + kafkaPublisher, kafkaSubscriber, err := r.SetupKafka() + if err != nil { + return err + } + + grafanaPublisher, err := r.SetupGrafana() + if err != nil { + return err + } + + r.Router.AddHandler( + "http-to-kafka", + "/", + httpSubscriber, + r.Config.KafkaTopic, + kafkaPublisher, + pkg.GithubWebhookHandler, + ) + + r.Router.AddHandler( + "rabbitmq-to-kafka", + r.Config.AMQPQueue, + amqpSubscriber, + r.Config.KafkaTopic, + kafkaPublisher, + pkg.AMQPHandler, + ) + + r.Router.AddHandler( + "kafka-to-grafana", + r.Config.KafkaTopic, + kafkaSubscriber, + r.Config.GrafanaURL+"/api/annotations", + grafanaPublisher, + pkg.GrafanaHandler, + ) + + stagingDelay := time.Second * time.Duration(rand.Intn(60)+30) + productionDelay := stagingDelay + time.Second*time.Duration(rand.Intn(120)+60) + + r.Router.AddHandler( + "deploy-staging-simulator", + r.Config.KafkaTopic, + kafkaSubscriber, + r.Config.AMQPQueue, + amqpPublisher, + pkg.DeploySimulator{Env: "staging", Delay: stagingDelay}.Handle, + ) + + r.Router.AddHandler( + "deploy-production-simulator", + r.Config.KafkaTopic, + kafkaSubscriber, + r.Config.AMQPQueue, + amqpPublisher, + pkg.DeploySimulator{Env: "production", Delay: productionDelay}.Handle, + ) + + go func() { + httpSub, ok := httpSubscriber.(*http.Subscriber) + if !ok { + panic("invalid subscriber type") + } + + <-r.Router.Running() + err = httpSub.StartHTTPServer() + if err != nil { + panic(err) + } + }() return nil } diff --git a/pkg/amqp.go b/pkg/amqp.go new file mode 100644 index 0000000..7a95f54 --- /dev/null +++ b/pkg/amqp.go @@ -0,0 +1,41 @@ +package pkg + +import ( + "encoding/json" + "log" + + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill/message" +) + +type deploymentPayload struct { + CommitID string `json:"commit_id"` + Env string `json:"env"` + Timestamp string `json:"timestamp"` +} + +func AMQPHandler(msg *message.Message) ([]*message.Message, error) { + payload := deploymentPayload{} + err := json.Unmarshal(msg.Payload, &payload) + if err != nil { + return nil, err + } + + log.Println("Received AMQP message: ", payload) + + event := CommitDeployed{ + ID: payload.CommitID, + Env: payload.Env, + OccurredOn: payload.Timestamp, + } + eventJSON, err := json.Marshal(event) + if err != nil { + return nil, err + } + + log.Println("Sending event:", event) + + m := message.NewMessage(watermill.NewUUID(), eventJSON) + m.Metadata.Set("event_type", "commitDeployed") + return []*message.Message{m}, nil +} diff --git a/pkg/events.go b/pkg/events.go index 19b4907..13ef963 100644 --- a/pkg/events.go +++ b/pkg/events.go @@ -1,5 +1,6 @@ package pkg +// * CommitPushed is the event that is sent when a commit is pushed to GitHub type CommitPushed struct { ID string `json:"id"` Message string `json:"message"` @@ -7,6 +8,7 @@ type CommitPushed struct { OccurredOn string `json:"occurred_on"` } +// * CommitDeployed is the event that is sent when a commit is deployed to an environment type CommitDeployed struct { ID string `json:"id"` Env string `json:"env"` diff --git a/pkg/github.go b/pkg/github.go new file mode 100644 index 0000000..eecd1c3 --- /dev/null +++ b/pkg/github.go @@ -0,0 +1,54 @@ +package pkg + +import ( + "encoding/json" + "log" + + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill/message" +) + +// * CommitPushed is the event that is sent when a commit is pushed to GitHub +type githubPushEvent struct { + Commits []struct { + ID string `json:"id"` + Message string `json:"message"` + Timestamp string `json:"timestamp"` + Author struct { + Name string `json:"name"` + } `json:"author"` + } `json:"commits"` +} + +// * GithubWebhookHandler handles GitHub webhook events +func GithubWebhookHandler(msg *message.Message) ([]*message.Message, error) { + pushEvent := githubPushEvent{} + err := json.Unmarshal(msg.Payload, &pushEvent) + if err != nil { + return nil, err + } + + log.Println("Received GitHub Webhook:", pushEvent) + + var messages []*message.Message + for _, commit := range pushEvent.Commits { + event := CommitPushed{ + ID: commit.ID, + Message: commit.Message, + Author: commit.Author.Name, + OccurredOn: commit.Timestamp, + } + eventJSON, err := json.Marshal(event) + if err != nil { + return nil, err + } + + log.Println("Sending push event", event) + + m := message.NewMessage(watermill.NewUUID(), eventJSON) + m.Metadata.Set("event_type", "commitPushed") + messages = append(messages, m) + } + + return messages, nil +} diff --git a/pkg/simulator.go b/pkg/simulator.go new file mode 100644 index 0000000..d5436b0 --- /dev/null +++ b/pkg/simulator.go @@ -0,0 +1,54 @@ +package pkg + +import ( + "encoding/json" + "log" + "time" + + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill/message" +) + +// * DeploySimulator simulates deployment messages for each commitPushed event. +type DeploySimulator struct { + Env string + Delay time.Duration +} + +// * Handle receives a commitPushed event, waits for chosen duration and produces a commitDeployed event. +func (d DeploySimulator) Handle(msg *message.Message) ([]*message.Message, error) { + if msg.Metadata.Get("event_type") != "commitPushed" { + return nil, nil + } + + event := CommitPushed{} + err := json.Unmarshal(msg.Payload, &event) + if err != nil { + return nil, err + } + + baseTime, err := time.Parse(time.RFC3339, event.OccurredOn) + if err != nil { + return nil, err + } + + log.Println("Received new event:", event) + + time.Sleep(d.Delay) + + payload := deploymentPayload{ + CommitID: event.ID, + Env: d.Env, + Timestamp: baseTime.Add(d.Delay).Format(time.RFC3339), + } + + log.Println("Sending event:", payload) + + payloadJSON, err := json.Marshal(payload) + if err != nil { + panic(err) + } + + m := message.NewMessage(watermill.NewUUID(), payloadJSON) + return []*message.Message{m}, nil +} From ea51c3918c5c83679668adc8ce81147c5b3328c3 Mon Sep 17 00:00:00 2001 From: Furkan-Gulsen Date: Sun, 19 Nov 2023 12:38:31 +0300 Subject: [PATCH 6/7] feat: change folder structure and dockerizing --- docker-compose.yml | 66 ++++++++++++++++++++++ metrics/grafana/datasources/prometheus.yml | 6 ++ metrics/grafana/provisioning/default.yml | 10 ++++ metrics/prometheus.yml | 9 +++ myapp/Dockerfile | 18 ++++++ {cmd => myapp/cmd}/app/main.go | 0 {config => myapp/config}/config.go | 0 go.mod => myapp/go.mod | 2 +- go.sum => myapp/go.sum | 0 {internal => myapp/internal}/router.go | 0 {pkg => myapp/pkg}/amqp.go | 0 {pkg => myapp/pkg}/events.go | 0 {pkg => myapp/pkg}/github.go | 0 {pkg => myapp/pkg}/grafana.go | 0 {pkg => myapp/pkg}/simulator.go | 0 15 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 docker-compose.yml create mode 100644 metrics/grafana/datasources/prometheus.yml create mode 100644 metrics/grafana/provisioning/default.yml create mode 100644 metrics/prometheus.yml create mode 100644 myapp/Dockerfile rename {cmd => myapp/cmd}/app/main.go (100%) rename {config => myapp/config}/config.go (100%) rename go.mod => myapp/go.mod (99%) rename go.sum => myapp/go.sum (100%) rename {internal => myapp/internal}/router.go (100%) rename {pkg => myapp/pkg}/amqp.go (100%) rename {pkg => myapp/pkg}/events.go (100%) rename {pkg => myapp/pkg}/github.go (100%) rename {pkg => myapp/pkg}/grafana.go (100%) rename {pkg => myapp/pkg}/simulator.go (100%) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3edbbd1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,66 @@ +version: '3' +services: + myapp: + build: ./myapp + restart: on-failure + ports: + - 8080:8080 + env_file: + - .env + depends_on: + - kafka + - rabbitmq + + grafana: + image: grafana/grafana + ports: + - 3000:3000 + volumes: + - grafana_storage:/var/lib/grafana + - ./metrics/grafana/provisioning:/etc/grafana/provisioning/dashboards + - ./metrics/grafana/datasources:/etc/grafana/provisioning/datasources + - ./metrics/grafana/dashboards:/var/lib/grafana/dashboards + environment: + GF_SECURITY_ADMIN_PASSWORD: secret + logging: + driver: none + + zookeeper: + image: confluentinc/cp-zookeeper:latest + restart: on-failure + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + logging: + driver: none + + kafka: + image: confluentinc/cp-kafka:latest + restart: on-failure + logging: + driver: none + depends_on: + - zookeeper + environment: + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true' + + rabbitmq: + image: rabbitmq:latest + restart: on-failure + ports: + - 5672:5672 + logging: + driver: none + + prometheus: + image: prom/prometheus + restart: on-failure + volumes: + - ./metrics/prometheus.yml:/etc/prometheus/prometheus.yml + logging: + driver: none + +volumes: + grafana_storage: {} diff --git a/metrics/grafana/datasources/prometheus.yml b/metrics/grafana/datasources/prometheus.yml new file mode 100644 index 0000000..f6a3d05 --- /dev/null +++ b/metrics/grafana/datasources/prometheus.yml @@ -0,0 +1,6 @@ +apiVersion: 1 +datasources: + - name: Prometheus + type: prometheus + url: http://prometheus:9090 + access: proxy diff --git a/metrics/grafana/provisioning/default.yml b/metrics/grafana/provisioning/default.yml new file mode 100644 index 0000000..f6a0df2 --- /dev/null +++ b/metrics/grafana/provisioning/default.yml @@ -0,0 +1,10 @@ +apiVersion: 1 + +providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + options: + path: /var/lib/grafana/dashboards diff --git a/metrics/prometheus.yml b/metrics/prometheus.yml new file mode 100644 index 0000000..c5c33b8 --- /dev/null +++ b/metrics/prometheus.yml @@ -0,0 +1,9 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'metrics' + static_configs: + - targets: + - 'app:8081' diff --git a/myapp/Dockerfile b/myapp/Dockerfile new file mode 100644 index 0000000..51ab4ef --- /dev/null +++ b/myapp/Dockerfile @@ -0,0 +1,18 @@ +# Step 1: Modules caching +FROM golang:1.21.4-alpine3.18 as modules +COPY go.mod go.sum /modules/ +WORKDIR /modules +RUN go mod download + +# Step 2: Builder +FROM golang:1.21.4-alpine3.18 as builder +COPY --from=modules /go/pkg /go/pkg +COPY . /app +WORKDIR /app +RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \ + go build -tags migrate -o /bin/app ./cmd/app + +# Step 3: Final +FROM scratch +COPY --from=builder /bin/app /app +CMD ["/app"] diff --git a/cmd/app/main.go b/myapp/cmd/app/main.go similarity index 100% rename from cmd/app/main.go rename to myapp/cmd/app/main.go diff --git a/config/config.go b/myapp/config/config.go similarity index 100% rename from config/config.go rename to myapp/config/config.go diff --git a/go.mod b/myapp/go.mod similarity index 99% rename from go.mod rename to myapp/go.mod index dea890a..ce9e798 100644 --- a/go.mod +++ b/myapp/go.mod @@ -1,6 +1,6 @@ module github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang -go 1.21.3 +go 1.21 require ( github.com/ThreeDotsLabs/watermill v1.3.5 diff --git a/go.sum b/myapp/go.sum similarity index 100% rename from go.sum rename to myapp/go.sum diff --git a/internal/router.go b/myapp/internal/router.go similarity index 100% rename from internal/router.go rename to myapp/internal/router.go diff --git a/pkg/amqp.go b/myapp/pkg/amqp.go similarity index 100% rename from pkg/amqp.go rename to myapp/pkg/amqp.go diff --git a/pkg/events.go b/myapp/pkg/events.go similarity index 100% rename from pkg/events.go rename to myapp/pkg/events.go diff --git a/pkg/github.go b/myapp/pkg/github.go similarity index 100% rename from pkg/github.go rename to myapp/pkg/github.go diff --git a/pkg/grafana.go b/myapp/pkg/grafana.go similarity index 100% rename from pkg/grafana.go rename to myapp/pkg/grafana.go diff --git a/pkg/simulator.go b/myapp/pkg/simulator.go similarity index 100% rename from pkg/simulator.go rename to myapp/pkg/simulator.go From 3d777b027deefbe49edddf43ecb953a6e10a9595 Mon Sep 17 00:00:00 2001 From: Furkan-Gulsen Date: Sun, 19 Nov 2023 21:45:25 +0300 Subject: [PATCH 7/7] Updated Legacy Package Versions and Restructured File Layout --- .env-example | 8 + LICENSE | 2 +- README.md | 53 +- {myapp => app}/Dockerfile | 0 {myapp => app}/cmd/app/main.go | 4 +- {myapp => app}/config/config.go | 4 + {myapp => app}/go.mod | 2 +- {myapp => app}/go.sum | 0 {myapp => app}/internal/router.go | 24 +- {myapp => app}/pkg/amqp.go | 0 {myapp => app}/pkg/events.go | 0 {myapp => app}/pkg/github.go | 0 {myapp => app}/pkg/grafana.go | 0 {myapp => app}/pkg/simulator.go | 0 app/pkg/slack.go | 79 ++ docker-compose.yml | 4 +- metrics/grafana/dashboards/watermill.json | 1295 +++++++++++++++++++++ metrics/grafana/dashboards/webhooks.json | 153 +++ scripts/send-stub-webhook.sh | 20 + 19 files changed, 1639 insertions(+), 9 deletions(-) create mode 100644 .env-example rename {myapp => app}/Dockerfile (100%) rename {myapp => app}/cmd/app/main.go (89%) rename {myapp => app}/config/config.go (94%) rename {myapp => app}/go.mod (97%) rename {myapp => app}/go.sum (100%) rename {myapp => app}/internal/router.go (88%) rename {myapp => app}/pkg/amqp.go (100%) rename {myapp => app}/pkg/events.go (100%) rename {myapp => app}/pkg/github.go (100%) rename {myapp => app}/pkg/grafana.go (100%) rename {myapp => app}/pkg/simulator.go (100%) create mode 100644 app/pkg/slack.go create mode 100644 metrics/grafana/dashboards/watermill.json create mode 100644 metrics/grafana/dashboards/webhooks.json create mode 100644 scripts/send-stub-webhook.sh diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..9c0c46d --- /dev/null +++ b/.env-example @@ -0,0 +1,8 @@ + defaultBindAddr=:8080 + defaultMetricsBindAddr=:8081 + defaultGrafanaURL=http://grafana:3000 + defaultGrafanaCredentials=admin:secret + defaultAMQPURI=amqp://guest:guest@rabbitmq:5672/ + defaultAMQPQueue=deployments + defaultKafkaBrokers=kafka:9092 + defaultKafkaTopic=events \ No newline at end of file diff --git a/LICENSE b/LICENSE index f0fe887..20c82ae 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Furkan Gulsen +Copyright (c) 2019 Three Dots Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 20366f6..0683293 100644 --- a/README.md +++ b/README.md @@ -1 +1,52 @@ -# Event Drivent Architecture with Golang +# Event Driven Architecture with Golang (Example) + +This is an example Event-Driven application written in Go, using [Watermill](https://github.com/ThreeDotsLabs/watermill). + +The projects aims to integrate incoming GitHub webhooks with Grafana and Slack, essentially adding annotations and +sending messages when a new commit is pushed. There are also simulated deployment messages sent over RabbitMQ to +demonstrate working with multiple event streams. + +![](https://threedots.tech/media/event-driven-applications/diagram.png) + +An example result can look like this: + +![](https://threedots.tech/media/event-driven-applications/grafana.png) + +![](https://threedots.tech/media/event-driven-applications/slack.png) + +## Running + +If you'd like to integrate the example with your Slack workspace, copy `.env-example` to `.env` and fill in the +webhook URL in `SLACK_WEBHOOK_URL` variable. + +In addition to the application, the docker-compose environment consists of: + +- **Kafka** and **ZooKeeper** +- **RabbitMQ** +- **Grafana** +- **Prometheus** + +The whole environment can be run with: + +```bash +docker-compose up +``` + +You can now configure your GitHub repository to send webhooks to the application (you need to expose port `8080` to the +external network first). + +Alternatively, you can run `./scripts/send-stub-webhook.sh` to send some stub webhooks. + +Visit [localhost:3000/d/webhooks](http://localhost:3000/d/webhooks) to see annotations added in Grafana. Use +`admin:secret` as credentials. + +## Metrics + +You can access the Watermill dashboard at [localhost:3000/d/watermill](http://localhost:3000/d/watermill). See what +changes when you send more webhooks over time. + +![](https://threedots.tech/media/event-driven-applications/metrics.png) + +## What's next? + +See [Watermill's documentation](https://watermill.io/) to learn more. diff --git a/myapp/Dockerfile b/app/Dockerfile similarity index 100% rename from myapp/Dockerfile rename to app/Dockerfile diff --git a/myapp/cmd/app/main.go b/app/cmd/app/main.go similarity index 89% rename from myapp/cmd/app/main.go rename to app/cmd/app/main.go index c94a6fe..3b953b5 100644 --- a/myapp/cmd/app/main.go +++ b/app/cmd/app/main.go @@ -3,8 +3,8 @@ package main import ( "context" - "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/config" - "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/internal" + "github.com/ThreeDotsLabs/event-driven-example/config" + "github.com/ThreeDotsLabs/event-driven-example/internal" "github.com/ThreeDotsLabs/watermill" "github.com/ThreeDotsLabs/watermill/components/metrics" "github.com/ThreeDotsLabs/watermill/message" diff --git a/myapp/config/config.go b/app/config/config.go similarity index 94% rename from myapp/config/config.go rename to app/config/config.go index 938b69b..a7d42ba 100644 --- a/myapp/config/config.go +++ b/app/config/config.go @@ -31,6 +31,8 @@ type Config struct { KafkaBrokers []string KafkaTopic string + + SlackWebhookURL string } func LoadConfig() Config { @@ -46,6 +48,8 @@ func LoadConfig() Config { GrafanaURL: getEnv("GRAFANA_URL", defaultGrafanaURL), GrafanaCredentials: getEnv("GRAFANA_CREDENTIALS", defaultGrafanaCredentials), + + SlackWebhookURL: getEnv("SLACK_WEBHOOK_URL", ""), } } diff --git a/myapp/go.mod b/app/go.mod similarity index 97% rename from myapp/go.mod rename to app/go.mod index ce9e798..f0adf60 100644 --- a/myapp/go.mod +++ b/app/go.mod @@ -1,4 +1,4 @@ -module github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang +module github.com/ThreeDotsLabs/event-driven-example go 1.21 diff --git a/myapp/go.sum b/app/go.sum similarity index 100% rename from myapp/go.sum rename to app/go.sum diff --git a/myapp/internal/router.go b/app/internal/router.go similarity index 88% rename from myapp/internal/router.go rename to app/internal/router.go index 5d300e1..ae1151a 100644 --- a/myapp/internal/router.go +++ b/app/internal/router.go @@ -5,8 +5,8 @@ import ( "math/rand" "time" - "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/config" - "github.com/Furkan-Gulsen/Event-Driven-Architecture-with-Golang/pkg" + "github.com/ThreeDotsLabs/event-driven-example/config" + "github.com/ThreeDotsLabs/event-driven-example/pkg" "github.com/ThreeDotsLabs/watermill" "github.com/ThreeDotsLabs/watermill-amqp/pkg/amqp" "github.com/ThreeDotsLabs/watermill-http/pkg/http" @@ -140,6 +140,26 @@ func (r *Router) SetupRouter() error { pkg.GrafanaHandler, ) + if r.Config.SlackWebhookURL != "" { + slackPublisher, err := http.NewPublisher( + http.PublisherConfig{ + MarshalMessageFunc: pkg.SlackMarshaller, + }, r.Logger) + if err != nil { + return err + } + + r.Router.AddHandler( + "kafka-to-slack", + r.Config.KafkaTopic, + kafkaSubscriber, + r.Config.SlackWebhookURL, + slackPublisher, + pkg.SlackHandler, + ) + } + + // * DeploySimulator is a handler that simulates a deployment to a given environment. stagingDelay := time.Second * time.Duration(rand.Intn(60)+30) productionDelay := stagingDelay + time.Second*time.Duration(rand.Intn(120)+60) diff --git a/myapp/pkg/amqp.go b/app/pkg/amqp.go similarity index 100% rename from myapp/pkg/amqp.go rename to app/pkg/amqp.go diff --git a/myapp/pkg/events.go b/app/pkg/events.go similarity index 100% rename from myapp/pkg/events.go rename to app/pkg/events.go diff --git a/myapp/pkg/github.go b/app/pkg/github.go similarity index 100% rename from myapp/pkg/github.go rename to app/pkg/github.go diff --git a/myapp/pkg/grafana.go b/app/pkg/grafana.go similarity index 100% rename from myapp/pkg/grafana.go rename to app/pkg/grafana.go diff --git a/myapp/pkg/simulator.go b/app/pkg/simulator.go similarity index 100% rename from myapp/pkg/simulator.go rename to app/pkg/simulator.go diff --git a/app/pkg/slack.go b/app/pkg/slack.go new file mode 100644 index 0000000..470b8ad --- /dev/null +++ b/app/pkg/slack.go @@ -0,0 +1,79 @@ +package pkg + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "net/http" + + "github.com/ThreeDotsLabs/watermill" + "github.com/ThreeDotsLabs/watermill/message" +) + +// * CommitPushed represents a commit pushed event. +type slackMessagePayload struct { + Text string `json:"text"` +} + +// * SlackMarshaller creates a new HTTP request with a JSON payload. +func SlackMarshaller(url string, msg *message.Message) (*http.Request, error) { + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(msg.Payload)) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + + return req, nil +} + +// * SlackHandler receives an event and translates it into a message payload compatible with Slack REST API. +func SlackHandler(msg *message.Message) ([]*message.Message, error) { + eventType := msg.Metadata.Get("event_type") + text, err := slackTextByType(eventType, msg.Payload) + if err != nil { + return nil, err + } + + payload := slackMessagePayload{ + Text: text, + } + + payloadJSON, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + log.Println("Sending Slack webhook:", string(payloadJSON)) + + m := message.NewMessage(watermill.NewUUID(), payloadJSON) + return []*message.Message{m}, nil +} + +// * slackTextByType returns a text message for a given event type. +func slackTextByType(eventType string, payload []byte) (string, error) { + switch eventType { + case "commitPushed": + event := CommitPushed{} + err := json.Unmarshal(payload, &event) + if err != nil { + return "", err + } + + text := fmt.Sprintf(":rocket: Commit `%s` *pushed* by %s: _%s_", + event.ID, event.Author, event.Message) + return text, nil + case "commitDeployed": + event := CommitDeployed{} + err := json.Unmarshal(payload, &event) + if err != nil { + return "", err + } + + text := fmt.Sprintf(":heavy_check_mark: Commit `%s` deployed to *%s*", event.ID, event.Env) + return text, nil + default: + return "", fmt.Errorf("unknown event type: %s", eventType) + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 3edbbd1..3e384ef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: - myapp: - build: ./myapp + app: + build: ./app restart: on-failure ports: - 8080:8080 diff --git a/metrics/grafana/dashboards/watermill.json b/metrics/grafana/dashboards/watermill.json new file mode 100644 index 0000000..9b3ffef --- /dev/null +++ b/metrics/grafana/dashboards/watermill.json @@ -0,0 +1,1295 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Watermill is a Go library for working efficiently with message streams. It is intended for building event driven applications, enabling event sourcing, RPC over messages, sagas and basically whatever else comes to your mind. You can use conventional pub/sub implementations like Kafka or RabbitMQ, but also HTTP or MySQL binlog if that fits your use case.", + "editable": true, + "gnetId": 9777, + "graphTooltip": 0, + "id": null, + "iteration": 1550057756355, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 12, + "panels": [], + "title": "Current stats", + "type": "row" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "Prometheus", + "decimals": 0, + "description": "Counts the messages received within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 0, + "y": 1 + }, + "id": 14, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "Value", + "targets": [ + { + "expr": "sum(increase(subscriber_messages_received_total{instance=~\"$node\", handler_name=~\"$handler\"}[$interval]))", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "Messages received", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "Prometheus", + "decimals": 0, + "description": "Counts the handler errors within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 3, + "y": 1 + }, + "id": 16, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(191, 27, 0, 0.33)", + "full": false, + "lineColor": "#bf1b00", + "show": true + }, + "tableColumn": "Value", + "targets": [ + { + "expr": "sum(increase(handler_execution_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\"}[$interval]))", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "Handler errors", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "Prometheus", + "decimals": 0, + "description": "Counts the messages publisher within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 6, + "y": 1 + }, + "id": 15, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(increase(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"true\"}[$interval]))", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "Messages published", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": "Prometheus", + "decimals": 0, + "description": "Counts the publisher errors within the selected interval. The graph shows the historical trend of this value, also measured over the selected interval.", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 9, + "y": 1 + }, + "id": 17, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(191, 27, 0, 0.33)", + "full": false, + "lineColor": "#bf1b00", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(increase(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\"}[$interval]))", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "Publisher errors", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["#d44a3a", "rgba(237, 129, 40, 0.89)", "#299c46"], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 12, + "y": 1 + }, + "id": 18, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(57, 232, 0, 0.5)", + "full": false, + "lineColor": "rgba(57, 232, 0, 1)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(count(subscriber_messages_received_total{acked=\"acked\"}) by (instance))", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "-1, 0", + "title": "Subscriber count", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["#d44a3a", "rgba(237, 129, 40, 0.89)", "#299c46"], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 15, + "y": 1 + }, + "id": 19, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(57, 232, 0, 0.5)", + "full": false, + "lineColor": "rgba(57, 232, 0, 1)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(count(handler_execution_time_seconds_count{success=\"true\"}) by (instance))", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "-1, 0", + "title": "Handler count", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["#d44a3a", "rgba(237, 129, 40, 0.89)", "#299c46"], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 18, + "y": 1 + }, + "id": 20, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(57, 232, 0, 0.5)", + "full": false, + "lineColor": "rgba(57, 232, 0, 1)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(count(publish_time_seconds_count{success=\"true\"}) by (instance))", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "-1, 0", + "title": "Publisher count", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "0", + "value": "null" + } + ], + "valueName": "current" + }, + { + "content": "
\n \n \n \n \n \n \n
", + "gridPos": { + "h": 4, + "w": 3, + "x": 21, + "y": 1 + }, + "id": 22, + "links": [], + "mode": "html", + "title": "Made by", + "type": "text" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 10, + "panels": [], + "title": "Handler & publisher operations", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(irate(handler_execution_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by (handler_name, instance)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{handler_name}}@{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Handler op/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(irate(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, instance, publisher_name)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{publisher_name}}@{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Publish op/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 3, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 5, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "repeatDirection": "h", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(handler_execution_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by (le))", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(handler_execution_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by ( le))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(handler_execution_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\"}[$interval])) by (le))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Handler duration @ $node nodes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": 1, + "fill": 3, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(publish_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, le, publisher_name))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "p99 {{handler_name}}→{{publisher_name}}", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(publish_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, le, publisher_name))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "p90 {{handler_name}}→{{publisher_name}}", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(publish_time_seconds_bucket{instance=~\"$node\", handler_name=~\"$handler\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, le, publisher_name))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "p50 {{handler_name}}→{{publisher_name}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Publish duration @ $node nodes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 23 + }, + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(irate(handler_execution_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\"}[$interval])) by (handler_name, instance)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{handler_name}}@{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Handler failed op/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 23 + }, + "id": 7, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(irate(publish_time_seconds_count{instance=~\"$node\", handler_name=~\"$handler\", success=\"false\", publisher_name=~\"$publisher\"}[$interval])) by (handler_name, instance)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{handler_name}}@{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Publish failed op/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transparent": false, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 16, + "style": "dark", + "tags": ["go", "golang", "cqrs", "kafka", "event-driven"], + "templating": { + "list": [ + { + "auto": false, + "auto_count": 30, + "auto_min": "15s", + "current": { + "text": "1m", + "value": "1m" + }, + "hide": 0, + "label": null, + "name": "interval", + "options": [ + { + "selected": false, + "text": "15s", + "value": "15s" + }, + { + "selected": false, + "text": "30s", + "value": "30s" + }, + { + "selected": true, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "10m", + "value": "10m" + }, + { + "selected": false, + "text": "30m", + "value": "30m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "12h", + "value": "12h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + } + ], + "query": "15s, 30s, 1m,10m,30m,1h,6h,12h,1d", + "refresh": 2, + "type": "interval" + }, + { + "allValue": "", + "current": {}, + "datasource": "Prometheus", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "node", + "options": [], + "query": "label_values(instance)", + "refresh": 2, + "regex": "", + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "Prometheus", + "hide": 0, + "includeAll": true, + "label": "handler", + "multi": true, + "name": "handler", + "options": [], + "query": "label_values(handler_name)", + "refresh": 2, + "regex": "", + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "Prometheus", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "publisher", + "options": [], + "query": "label_values(publisher_name)", + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "", + "title": "Watermill", + "uid": "watermill", + "version": 1 +} diff --git a/metrics/grafana/dashboards/webhooks.json b/metrics/grafana/dashboards/webhooks.json new file mode 100644 index 0000000..384df55 --- /dev/null +++ b/metrics/grafana/dashboards/webhooks.json @@ -0,0 +1,153 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + }, + { + "datasource": "-- Grafana --", + "enable": true, + "hide": false, + "iconColor": "#ef843c", + "limit": 100, + "name": "pushed", + "showIn": 0, + "tags": ["pushed"], + "type": "tags" + }, + { + "datasource": "-- Grafana --", + "enable": true, + "hide": false, + "iconColor": "#e5ac0e", + "limit": 100, + "name": "deploy-staging", + "showIn": 0, + "tags": ["deploy-staging"], + "type": "tags" + }, + { + "datasource": "-- Grafana --", + "enable": true, + "hide": false, + "iconColor": "#9ac48a", + "limit": 100, + "name": "deploy-production", + "showIn": 0, + "tags": ["deploy-production"], + "type": "tags" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fill": 1, + "gridPos": { + "h": 13, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Load", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "schemaVersion": 16, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "", + "title": "Webhooks", + "uid": "webhooks", + "version": 4 +} diff --git a/scripts/send-stub-webhook.sh b/scripts/send-stub-webhook.sh new file mode 100644 index 0000000..d9f6b6e --- /dev/null +++ b/scripts/send-stub-webhook.sh @@ -0,0 +1,20 @@ +#!/bin/bash +readonly payload="$(dirname "$0")/payload.json" +readonly time=$(date --rfc-3339=seconds | tr ' ' 'T') +readonly id=$(tr -dc 'a-f0-9' < /dev/urandom | head -c40) + +cat >"$payload" <