From 3a360c2973b3d24a2343e98ab2b3693f3d553cdb Mon Sep 17 00:00:00 2001 From: Mark Fussell Date: Fri, 15 Jan 2021 17:03:21 -0800 Subject: [PATCH] update changes for RC2 release --- .gitignore | 76 +- LICENSE | 42 +- README.md | 86 +- apim-gateway/README.md | 1298 +- apim-gateway/apim/api.yaml | 98 +- apim-gateway/apim/policy-all.json | 10 +- apim-gateway/apim/policy-echo.json | 10 +- apim-gateway/apim/policy-message.json | 10 +- apim-gateway/apim/policy-save.json | 10 +- apim-gateway/k8s/binding.yaml | 24 +- apim-gateway/k8s/echo-service.yaml | 68 +- apim-gateway/k8s/event-subscriber.yaml | 70 +- apim-gateway/k8s/gateway.yaml | 140 +- apim-gateway/k8s/pubsub.yaml | 28 +- autoscaling-on-queue/README.md | 410 +- .../deployment/kafka-client.yaml | 22 +- .../deployment/kafka-pubsub.yaml | 28 +- .../deployment/keda-2.0.0-beta.yaml | 14288 ++++++++-------- autoscaling-on-queue/deployment/producer.yaml | 78 +- .../deployment/subscriber-scaler.yaml | 34 +- .../deployment/subscriber.yaml | 66 +- autoscaling-on-queue/producer/Dockerfile | 28 +- autoscaling-on-queue/producer/Makefile | 80 +- autoscaling-on-queue/producer/go.mod | 22 +- autoscaling-on-queue/producer/go.sum | 210 +- autoscaling-on-queue/producer/main.go | 414 +- autoscaling-on-queue/subscriber/Dockerfile | 28 +- autoscaling-on-queue/subscriber/Makefile | 142 +- autoscaling-on-queue/subscriber/go.mod | 22 +- autoscaling-on-queue/subscriber/go.sum | 212 +- autoscaling-on-queue/subscriber/main.go | 244 +- component-api/README.md | 392 +- component-api/config/email.yaml | 28 +- component-api/config/state.yaml | 28 +- component-api/config/twitter.yaml | 48 +- component-api/sample/email.json | 14 +- component-api/sample/twitter.json | 14 +- cron-binding/Dockerfile | 28 +- cron-binding/Makefile | 116 +- cron-binding/README.md | 158 +- cron-binding/config/cron.yaml | 16 +- cron-binding/go.mod | 10 +- cron-binding/go.sum | 194 +- cron-binding/k8s/component.yaml | 20 +- cron-binding/k8s/deployment.yaml | 66 +- cron-binding/main.go | 96 +- dapr-aci/README.md | 348 +- dapr-aci/components/pubsub.yaml | 18 +- dapr-aci/components/state.yaml | 26 +- dapr-aci/deployment/app.yaml | 132 +- dapr-aci/src/Dockerfile | 28 +- dapr-aci/src/Makefile | 118 +- dapr-aci/src/go.mod | 24 +- dapr-aci/src/go.sum | 244 +- dapr-aci/src/main.go | 200 +- dapr-api-on-aci/README.md | 258 +- dapr-api-on-aci/email.yaml | 24 +- daprized-ingress/README.md | 332 +- .../config/ingress-annotations.yaml | 16 +- daprized-ingress/config/ingress-config.yaml | 46 +- daprized-ingress/config/ingress.yaml | 32 +- daprized-ingress/config/namespace.yml | 38 +- fan-out/README.md | 302 +- fan-out/grpc-echo-service/Dockerfile | 28 +- fan-out/grpc-echo-service/Makefile | 138 +- fan-out/grpc-echo-service/README.md | 152 +- fan-out/grpc-echo-service/deployment.yaml | 68 +- fan-out/grpc-echo-service/go.mod | 10 +- fan-out/grpc-echo-service/go.sum | 200 +- fan-out/grpc-echo-service/main.go | 110 +- fan-out/http-format-converter/Dockerfile | 28 +- fan-out/http-format-converter/Makefile | 114 +- .../config/source-pubsub.yaml | 20 +- .../config/target-binding.yaml | 20 +- fan-out/http-format-converter/go.mod | 16 +- fan-out/http-format-converter/go.sum | 200 +- .../http-format-converter/k8s/components.yaml | 32 +- .../http-format-converter/k8s/deployment.yaml | 74 +- fan-out/http-format-converter/main.go | 238 +- fan-out/queue-event-consumer/Dockerfile | 28 +- fan-out/queue-event-consumer/Makefile | 114 +- .../config/source-pabusb.yaml | 20 +- fan-out/queue-event-consumer/go.mod | 10 +- fan-out/queue-event-consumer/go.sum | 200 +- .../queue-event-consumer/k8s/deployment.yaml | 74 +- .../k8s/target-kafka.yaml | 24 +- .../k8s/target-redis.yaml | 24 +- fan-out/queue-event-consumer/main.go | 100 +- fan-out/queue-event-producer/Dockerfile | 28 +- fan-out/queue-event-producer/Makefile | 112 +- .../config/target-pabusb.yaml | 20 +- fan-out/queue-event-producer/go.mod | 18 +- fan-out/queue-event-producer/go.sum | 204 +- .../queue-event-producer/k8s/deployment.yaml | 74 +- .../k8s/target-kafka.yaml | 24 +- .../k8s/target-redis.yaml | 24 +- fan-out/queue-event-producer/main.go | 216 +- fan-out/queue-format-converter/Dockerfile | 28 +- fan-out/queue-format-converter/Makefile | 114 +- .../queue-format-converter/config/kafka.yaml | 26 +- .../config/source-pabusb.yaml | 20 +- .../config/target-pubsub.yaml | 20 +- fan-out/queue-format-converter/go.mod | 16 +- fan-out/queue-format-converter/go.sum | 200 +- .../k8s/deployment.yaml | 74 +- .../k8s/target-kafka.yaml | 24 +- .../k8s/target-redis.yaml | 24 +- fan-out/queue-format-converter/main.go | 220 +- fan-out/service-format-converter/Dockerfile | 28 +- fan-out/service-format-converter/Makefile | 124 +- .../config/source-pubsub.yaml | 20 +- fan-out/service-format-converter/go.mod | 16 +- fan-out/service-format-converter/go.sum | 200 +- .../k8s/components.yaml | 32 +- .../k8s/deployment.yaml | 74 +- fan-out/service-format-converter/main.go | 234 +- grpc-echo-service/Dockerfile | 28 +- grpc-echo-service/Makefile | 130 +- grpc-echo-service/README.md | 172 +- grpc-echo-service/deployment/app.yaml | 64 +- grpc-echo-service/deployment/space.yaml | 98 +- grpc-echo-service/go.mod | 18 +- grpc-echo-service/go.sum | 234 +- grpc-echo-service/main.go | 110 +- grpc-event-subscriber/Dockerfile | 28 +- grpc-event-subscriber/Makefile | 166 +- grpc-event-subscriber/README.md | 170 +- grpc-event-subscriber/config/pubsub.yaml | 20 +- grpc-event-subscriber/go.mod | 10 +- grpc-event-subscriber/go.sum | 200 +- grpc-event-subscriber/k8s/component.yaml | 34 +- grpc-event-subscriber/k8s/deployment.yaml | 74 +- grpc-event-subscriber/main.go | 112 +- hardened/Makefile | 72 +- hardened/README.md | 715 +- hardened/config/pubsub.yaml | 36 +- hardened/config/state.yaml | 26 +- hardened/deployment/Monitoring/ns.yaml | 4 + hardened/deployment/Monitoring/zipkin.yaml | 38 + hardened/deployment/hardened/app1.yaml | 126 +- hardened/deployment/hardened/app2.yaml | 132 +- hardened/deployment/hardened/app3.yaml | 104 +- hardened/deployment/hardened/pubsub.yaml | 40 +- hardened/deployment/hardened/state.yaml | 30 +- hardened/deployment/namespace/ns.yaml | 8 +- hardened/deployment/namespace/role.yaml | 42 +- hardened/deployment/namespace/trace.yaml | 12 - hardened/deployment/simple/app1.yaml | 72 +- hardened/deployment/simple/app2.yaml | 78 +- hardened/deployment/simple/app3.yaml | 72 +- hardened/deployment/simple/pubsub.yaml | 26 +- hardened/deployment/simple/state.yaml | 30 +- hardened/src/app1/Dockerfile | 28 +- hardened/src/app1/Makefile | 96 +- hardened/src/app1/go.mod | 16 +- hardened/src/app1/go.sum | 228 +- hardened/src/app1/main.go | 150 +- hardened/src/app2/Dockerfile | 28 +- hardened/src/app2/Makefile | 92 +- hardened/src/app2/go.mod | 16 +- hardened/src/app2/go.sum | 228 +- hardened/src/app2/main.go | 244 +- hardened/src/app3/Dockerfile | 28 +- hardened/src/app3/Makefile | 92 +- hardened/src/app3/go.mod | 16 +- hardened/src/app3/go.sum | 228 +- hardened/src/app3/main.go | 152 +- http-echo-service/Dockerfile | 28 +- http-echo-service/Makefile | 138 +- http-echo-service/README.md | 152 +- http-echo-service/deployment.yaml | 68 +- http-echo-service/go.mod | 10 +- http-echo-service/go.sum | 192 +- http-echo-service/main.go | 104 +- http-event-subscriber/Dockerfile | 28 +- http-event-subscriber/Makefile | 180 +- http-event-subscriber/README.md | 174 +- http-event-subscriber/clooudevent.json | 22 +- http-event-subscriber/config/pubsub.yaml | 22 +- http-event-subscriber/go.mod | 10 +- http-event-subscriber/go.sum | 192 +- http-event-subscriber/k8s/component.yaml | 34 +- http-event-subscriber/k8s/deployment.yaml | 74 +- http-event-subscriber/main.go | 120 +- order-cancellation/README.md | 190 +- order-cancellation/demo/Makefile | 66 +- order-cancellation/demo/README.md | 256 +- .../demo/component/audit-store.yaml | 42 +- order-cancellation/demo/component/email.yaml | 30 +- order-cancellation/demo/component/queue.yaml | 34 +- .../demo/component/workflow-store.yaml | 44 +- .../demo/config/order-cancel.json | 220 +- .../demo/data/cancellation.json | 62 +- order-cancellation/demo/data/email.json | 14 +- order-cancellation/demo/data/meta.json | 8 +- .../demo/deployment/auditor.yaml | 74 +- .../demo/deployment/ingress.yaml | 40 +- order-cancellation/demo/deployment/space.yaml | 98 +- .../demo/deployment/viewer.yaml | 98 +- .../demo/deployment/workflow.yaml | 100 +- order-cancellation/src/fn/.gitignore | 14 +- order-cancellation/src/fn/DaprAzFn.csproj | 46 +- order-cancellation/src/fn/Dockerfile | 26 +- order-cancellation/src/fn/Makefile | 66 +- order-cancellation/src/fn/ReceiveEvent.cs | 88 +- order-cancellation/src/fn/config/meta.json | 8 +- order-cancellation/src/fn/config/pubsub.yaml | 22 +- .../src/fn/config/statestore.yaml | 22 +- order-cancellation/src/fn/host.json | 4 +- order-cancellation/src/fn/local.settings.json | 18 +- order-cancellation/src/viewer/Dockerfile | 40 +- order-cancellation/src/viewer/Makefile | 102 +- .../src/viewer/config/queue.yaml | 18 +- order-cancellation/src/viewer/go.mod | 20 +- order-cancellation/src/viewer/go.sum | 202 +- order-cancellation/src/viewer/main.go | 242 +- .../src/viewer/resource/static/img/dapr.svg | 28 +- .../src/viewer/resource/static/img/tw.svg | 4 +- .../src/viewer/resource/static/js/app.js | 108 +- .../src/viewer/resource/template/footer.html | 36 +- .../src/viewer/resource/template/header.html | 54 +- .../src/viewer/resource/template/index.html | 48 +- pipeline/README.md | 614 +- pipeline/k8s/ingress.yaml | 40 +- pipeline/k8s/process-pubsub.yaml | 36 +- pipeline/k8s/processor.yaml | 82 +- pipeline/k8s/provider.yaml | 72 +- pipeline/k8s/scorer.yaml | 82 +- pipeline/k8s/space.yaml | 96 +- pipeline/k8s/state.yaml | 42 +- pipeline/k8s/tweeter-pubsub.yaml | 36 +- pipeline/k8s/twitter.yaml | 54 +- pipeline/k8s/viewer.yaml | 100 +- pipeline/sentiment-scorer/Dockerfile | 28 +- pipeline/sentiment-scorer/Makefile | 128 +- pipeline/sentiment-scorer/README.md | 138 +- pipeline/sentiment-scorer/config/secret.yaml | 16 +- pipeline/sentiment-scorer/go.mod | 22 +- pipeline/sentiment-scorer/go.sum | 212 +- pipeline/sentiment-scorer/main.go | 376 +- pipeline/tweet-processor/Dockerfile | 28 +- pipeline/tweet-processor/Makefile | 108 +- .../tweet-processor/config/result-pubsub.yaml | 20 +- .../tweet-processor/config/source-pubsub.yaml | 20 +- pipeline/tweet-processor/go.mod | 22 +- pipeline/tweet-processor/go.sum | 212 +- pipeline/tweet-processor/main.go | 322 +- pipeline/tweet-provider/Dockerfile | 28 +- pipeline/tweet-provider/Makefile | 90 +- pipeline/tweet-provider/README.md | 170 +- pipeline/tweet-provider/config/pubsub.yaml | 20 +- pipeline/tweet-provider/config/secret.yaml | 16 +- pipeline/tweet-provider/config/state.yaml | 20 +- pipeline/tweet-provider/config/twitter.yaml | 44 +- pipeline/tweet-provider/go.mod | 22 +- pipeline/tweet-provider/go.sum | 212 +- pipeline/tweet-provider/main.go | 152 +- pipeline/tweet-viewer/Dockerfile | 40 +- pipeline/tweet-viewer/Makefile | 116 +- .../tweet-viewer/config/source-pubsub.yaml | 20 +- pipeline/tweet-viewer/go.mod | 20 +- pipeline/tweet-viewer/go.sum | 222 +- pipeline/tweet-viewer/main.go | 230 +- .../tweet-viewer/resource/static/img/dapr.svg | 28 +- .../tweet-viewer/resource/static/img/tw.svg | 4 +- .../tweet-viewer/resource/static/js/app.js | 146 +- .../resource/template/footer.html | 36 +- .../resource/template/header.html | 54 +- .../tweet-viewer/resource/template/index.html | 22 +- pipeline/tweet-viewer/tweet.json | 214 +- setup/Makefile | 620 +- setup/README.md | 232 +- setup/aks/Makefile | 133 +- setup/aks/README.md | 126 +- setup/config/actor-dashboard.json | 1748 +- setup/config/fluentd-config.yaml | 208 +- setup/config/fluentd.yaml | 194 +- setup/config/ingress-annotations.yaml | 16 +- setup/config/ingress-config.yaml | 40 +- setup/config/ingress-template.yaml | 42 +- setup/config/namespace-template.yml | 78 +- setup/config/sidecar-dashboard.json | 4688 ++--- setup/config/system-services-dashboard.json | 3078 ++-- setup/config/zipkin.yaml | 74 +- setup/gke/Makefile | 130 +- setup/gke/README.md | 124 +- setup/ingress.yaml | 21 + setup/ingress2.yaml | 22 + setup/kubectl-install-dapr | 238 +- state-change-handler/Dockerfile | 28 +- state-change-handler/Makefile | 106 +- state-change-handler/README.md | 92 +- state-change-handler/config/pubsub.yaml | 22 +- state-change-handler/config/statechange.yaml | 32 +- state-change-handler/go.mod | 16 +- state-change-handler/go.sum | 200 +- state-change-handler/main.go | 134 +- 297 files changed, 26227 insertions(+), 26128 deletions(-) create mode 100644 hardened/deployment/Monitoring/ns.yaml create mode 100644 hardened/deployment/Monitoring/zipkin.yaml delete mode 100644 hardened/deployment/namespace/trace.yaml create mode 100644 setup/ingress.yaml create mode 100644 setup/ingress2.yaml diff --git a/.gitignore b/.gitignore index 61a28c8..f256ad3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,38 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# VS Code -Debug - -# Test binary, built with `go test -c` -*.test - -# Output -*.out - -# mac -.DS_Store - -# bin -bin - -# vendored dependnacies -vendor - -# certs -*.pem - -# secrets -secrets.json - -# certs -certs -setup/certs - -# other -setup/config/ingress.yaml -setup/config/namespace.yml +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# VS Code +Debug + +# Test binary, built with `go test -c` +*.test + +# Output +*.out + +# mac +.DS_Store + +# bin +bin + +# vendored dependnacies +vendor + +# certs +*.pem + +# secrets +secrets.json + +# certs +certs +setup/certs + +# other +setup/config/ingress.yaml +setup/config/namespace.yml diff --git a/LICENSE b/LICENSE index ff6506f..7857f46 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2020 Mark Chmarny - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2020 Mark Chmarny + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index cf13c7a..ce3f311 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,43 @@ -# Dapr demos - -Collection of personal [Dapr](https://dapr.io) demos. - -> Note, some of these demos require latest version of Dapr, Ingress gateway, Observability components, or cluster-local Redis and Mongo services. To create Kubernetes cluster with all these components on AKS, or quickly configure an existing cluster, consider the [Dapr Cluster Setup](./setup) utility. - - -* Bindings - * [Scheduling using cron](./cron-binding) - Using scheduler to execute service - * [Tweet stream](./pipeline/tweet-provider) - Subscribing to a Twitter even stream and publishing to a pub/sub topic - * [State change handler](./state-change-handler) - RethinkDB state changes streamed into topic -* Eventing - * [gRPC event subscriber](./grpc-event-subscriber) - Subscribing to topic and processing its events using gRPC service - * [HTTP event subscriber](./http-event-subscriber) - Subscribing to topic and processing its events using HTTP service -* Services - * [gRPC echo service](./grpc-echo-service) - gRPC service invocation example - * [HTTP echo service](./http-echo-service) - HTTP service invocation example - * [Sentiment Scorer](./pipeline/sentiment-scorer) - Sentiment scoring serving backed by Azure Cognitive Service -* Integrations - * [Components in ACI](./dapr-api-on-aci) - Dapr components as microservices on ACI without app - * [Component in Kubernetes](./component-api) - Dapr components as microservices on Kubernetes without app - * [Dapr Apps in ACI](./dapr-aci) - Deploying apps with Dapr sidecar and components on ACI - * [Dapr with APIM](./apim-gateway) - Dapr API using Azure API Management self-hosted gateway - * [Dapr API on Ingress](./daprized-ingress) - Expose Dapr API on Kubernetes Ingress Controller using NGINX - * [Dapr GitOps](https://github.com/mchmarny/git-ops) - GitHub Actions build pipeline for Dapr apps -* Solutions - * [Order cancellation](./order-cancellation) - multiple Dapr service integrations with observability - * [Pipeline](./pipeline) - Demos combining Twitter binding, Sentiment scoring, Multi Pub/Sub Processor, and WebSocket Viewer app - * [Fan-out](./fan-out) - Single message source "broadcasted" to multiple, configurable targets (e.g. Redis PubSub, HTTP, gRPC) - * [Hardened](./hardened) - Example of multi-microservice app with tightly controlled access to secrets, components, and full invoking service identity validation -* Templates - * [Dapr gRPC Service](https://github.com/mchmarny/dapr-grpc-service-template) - gRPC service template - * [Dapr HTTP Event Subscriber](https://github.com/mchmarny/dapr-http-event-subscriber-template) - Event subscriber HTTP service template - * [Dapr gRPC Event Subscriber](https://github.com/mchmarny/dapr-grpc-event-subscriber-template) - Event subscriber gRPC service template - * [dapr-http-cron-handler](https://github.com/mchmarny/dapr-http-cron-handler-template) - Scheduled service development template - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](./LICENSE) +# Dapr demos + +Collection of personal [Dapr](https://dapr.io) demos. + +> Note, some of these demos require latest version of Dapr, Ingress gateway, Observability components, or cluster-local Redis and Mongo services. To create Kubernetes cluster with all these components on AKS, or quickly configure an existing cluster, consider the [Dapr Cluster Setup](./setup) utility. + + +* Bindings + * [Scheduling using cron](./cron-binding) - Using scheduler to execute service + * [Tweet stream](./pipeline/tweet-provider) - Subscribing to a Twitter even stream and publishing to a pub/sub topic + * [State change handler](./state-change-handler) - RethinkDB state changes streamed into topic +* Eventing + * [gRPC event subscriber](./grpc-event-subscriber) - Subscribing to topic and processing its events using gRPC service + * [HTTP event subscriber](./http-event-subscriber) - Subscribing to topic and processing its events using HTTP service +* Services + * [gRPC echo service](./grpc-echo-service) - gRPC service invocation example + * [HTTP echo service](./http-echo-service) - HTTP service invocation example + * [Sentiment Scorer](./pipeline/sentiment-scorer) - Sentiment scoring serving backed by Azure Cognitive Service +* Integrations + * [Components in ACI](./dapr-api-on-aci) - Dapr components as microservices on ACI without app + * [Component in Kubernetes](./component-api) - Dapr components as microservices on Kubernetes without app + * [Dapr Apps in ACI](./dapr-aci) - Deploying apps with Dapr sidecar and components on ACI + * [Dapr with APIM](./apim-gateway) - Dapr API using Azure API Management self-hosted gateway + * [Dapr API on Ingress](./daprized-ingress) - Expose Dapr API on Kubernetes Ingress Controller using NGINX + * [Dapr GitOps](https://github.com/mchmarny/git-ops) - GitHub Actions build pipeline for Dapr apps +* Solutions + * [Order cancellation](./order-cancellation) - multiple Dapr service integrations with observability + * [Pipeline](./pipeline) - Demos combining Twitter binding, Sentiment scoring, Multi Pub/Sub Processor, and WebSocket Viewer app + * [Fan-out](./fan-out) - Single message source "broadcasted" to multiple, configurable targets (e.g. Redis PubSub, HTTP, gRPC) + * [Hardened](./hardened) - Example of multi-microservice app with tightly controlled access to secrets, components, and full invoking service identity validation +* Templates + * [Dapr gRPC Service](https://github.com/mchmarny/dapr-grpc-service-template) - gRPC service template + * [Dapr HTTP Event Subscriber](https://github.com/mchmarny/dapr-http-event-subscriber-template) - Event subscriber HTTP service template + * [Dapr gRPC Event Subscriber](https://github.com/mchmarny/dapr-grpc-event-subscriber-template) - Event subscriber gRPC service template + * [dapr-http-cron-handler](https://github.com/mchmarny/dapr-http-cron-handler-template) - Scheduled service development template + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](./LICENSE) diff --git a/apim-gateway/README.md b/apim-gateway/README.md index bcfdde6..a11c860 100644 --- a/apim-gateway/README.md +++ b/apim-gateway/README.md @@ -1,649 +1,649 @@ -# Dapr & Azure API Management Integration Demo - -Dapr integration with [Azure API Management](https://azure.microsoft.com/en-us/services/api-management/) (APIM) using self-hosted gateway on Kubernetes. - -![APIM Self-hosted Gateway Overview](img/overview-diagram.png) - -In this demo we will walk through the configuration of API Management service and its self-hosted gateway on Kubernetes. To illustrate the Dapr integration we will also review three Dapr use-cases: - -* Invocation of a specific Dapr service method -* Publishing content to a Pub/Sub topic -* Binding invocation with request content transformation - -In addition, we will overview the use of APIM tracing to debug your configuration. - -> While you can accomplish everything we show here in Azure portal, to make this demo easier to reliably reproduce, we will be using only the Azure CLI and APIs. - -## Prerequisite - -* [Azure account](https://azure.microsoft.com/en-us/free/) -* [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) -* [Kubernetes cluster with Dapr](https://github.com/dapr/docs/blob/v0.9.0/getting-started/environment-setup.md#installing-dapr-on-a-kubernetes-cluster) -* [Helm](https://helm.sh/docs/intro/install/) - -## Terminology - -You will see a few different APIs being used throughout this demo. At one point we are even going to use one API to manage another (API inception?). Here is short summary to help you keep all these APIs straight. Hope it helps: - -* [Azure API](https://docs.microsoft.com/en-us/rest/api/apimanagement/) - this is the API provided by Azure to manage its service (yes, including the API Management Service) -* [API in APIM](https://docs.microsoft.com/en-us/azure/api-management/edit-api) - is the API which we will define in APIM service. Its operations will be used by the users -* [Dapr API](https://github.com/dapr/docs/tree/master/reference/api#dapr-api-reference) - are the RESTful HTTP APIs defined by Dapr which developers interact with building applications - -## Setup - -To make this demo easier to reproduce, start by exporting the name for your new Azure API Management (APIM) service. - -> Note, the name of your API Management service instance has to be globally unique! - -```shell -export APIM_SERVICE_NAME="dapr-apim-demo" -``` - -In addition also export the Azure [Subscription ID](https://docs.bitnami.com/azure/faq/administration/find-subscription-id/) and [Resource Group](https://docs.bitnami.com/azure/faq/administration/find-deployment-resourcegroup-id/) where you want to create that service. - -```shell -export AZ_SUBSCRIPTION_ID="your-subscription-id" -export AZ_RESOURCE_GROUP="your-resource-group" -``` - -## Azure API Management - -We will start by configuring the Azure API Management service. - -### Service Creation - -Create service instance: - -> The `publisher-email` and `publisher-name` are only required to receive system notifications e-mails. - -```shell -az apim create --name $APIM_SERVICE_NAME \ - --subscription $AZ_SUBSCRIPTION_ID \ - --resource-group $AZ_RESOURCE_GROUP \ - --publisher-email "you@your-domain.com" \ - --publisher-name "Your Name" -``` - -> Note, depending on the SKU and resource group configuration, this operation may take 15+ min. While this running, consider quick read on [API Management Concepts](https://docs.microsoft.com/en-us/azure/api-management/api-management-key-concepts#-apis-and-operations) - -### API Configuration - -Each [API operation](https://docs.microsoft.com/en-us/azure/api-management/api-management-key-concepts#-apis-and-operations) defined in APIM will map to one Dapr API. To define these mappings you will use OpenAPI format defined in [apim/api.yaml](./apim/api.yaml) file. You will need to update the OpenAPI file with the name of the APIM service created above: - -```yaml -servers: - - url: http://.azure-api.net - - url: https://.azure-api.net -``` - -When finished, import that OpenAPI definition fle into APIM service instance: - -```shell -az apim api import --path / \ - --api-id dapr \ - --subscription $AZ_SUBSCRIPTION_ID \ - --resource-group $AZ_RESOURCE_GROUP \ - --service-name $APIM_SERVICE_NAME \ - --display-name "Demo Dapr Service API" \ - --protocols http https \ - --subscription-required true \ - --specification-path apim/api.yaml \ - --specification-format OpenApi -``` - -> Notice the `subscription-required` parameter is set to `true` which means that all invocations against the `dapr` API will need a subscription key. We cover how to obtain the subscription key later. - -### Azure API Token - -Export the Azure management API token to use through this demo. - -```shell -export AZ_API_TOKEN=$(az account get-access-token --resource=https://management.azure.com --query accessToken --output tsv) -``` - -> If you receive an error later that your token expired, just re-run this command - -### Policy Management - -APIM [Policies](https://docs.microsoft.com/en-us/azure/api-management/api-management-key-concepts#--policies) are sequentially executed on each request. We will start by defining "global" policy to throttle all operation invocations on our API, then add individual policies for each operation to add specific options. - -#### Global Policy - -APIM policies are defined inside of inbound, outbound, and backend elements. In our case to apply policy that will rate-limit all requests on all operations (before they are forwarded to Dapr API), we will place the global policy within the `inbound` section. - -> Note, the rate limit quota we defined here is being shared across all the replicas of self-hosted gateway. In default configuration, where there are 2 replicas, this policy would actually be half of the permitted calls per minute. - -```xml - - - - - ... - -``` - -Apply that [policy](apim/policy-all.json) to all operations submit it to the Azure management API. - -```shell -curl -i -X PUT \ - -d @apim/policy-all.json \ - -H "Content-Type: application/json" \ - -H "If-Match: *" \ - -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/policies/policy?api-version=2019-12-01" -``` - -If everything goes well, the management API will return the created policy. - -#### Echo Service Policy - -The Dapr service invocation handles all the service discovery, so to invoke a specific method on any Dapr service users follow this API: - -```http -POST/GET/PUT/DELETE /v1.0/invoke//method/ -``` - -To enable users to invoke the `echo` method on Dapr service with ID of `echo-service` we will create a policy that inherits the global policy (``) first, to ensure only authorize service invocation are passed to the backend Dapr API. Then to "map" the invocation we set `dapr` as the "backend-id" and define the Dapr service and method attributes to specific service ID and method name. - -```xml - - - - - - ... - -``` - -To apply [this policy](apim/policy-echo.json) to the `echo` operation on our API, submit it to the Azure management API: - -```shell -curl -i -X PUT \ - -d @apim/policy-echo.json \ - -H "Content-Type: application/json" \ - -H "If-Match: *" \ - -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/operations/echo/policies/policy?api-version=2019-12-01" -``` - -If everything goes well, the management API will return the created policy. Additional information about Dapr Service Invocation in APIM are available [here](https://aka.ms/apim/dapr/invoke). - -Also, since the external mapping of the API user invocations to Dapr is done in APIM policy, it can be easily re-mapped to any other version as the API implementation evolves over time. - -![](img/backend-policy.png) - -#### Message Topic Policy - -In addition to Dapr service invocation, APIM can also be used to publish to Dapr Pub/Sub API: - -```http -POST /v1.0/publish// -``` - -To expose the `messages` topic configured in the `demo-events` component we will start by inheriting the global policy like before, and then set the publish policy to format the request that will be passed to the Dapr Pub/Sub API: - -```xml - - - - @(context.Request.Body.As()) - - - ... - -``` - -To apply [this policy](apim/policy-message.json) to the `message` operation on our API, submit it to the Azure management API: - - -```shell -curl -i -X PUT \ - -d @apim/policy-message.json \ - -H "Content-Type: application/json" \ - -H "If-Match: *" \ - -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/operations/message/policies/policy?api-version=2019-12-01" -``` - -If everything goes well, the management API will return the created policy. Additional information about Dapr Pub/Sub support in APIM are available [here](https://aka.ms/apim/dapr/pubsub). - -#### Save Binding Policy - -In our final case, we are going to overview exposing the Dapr binding API. - -```http -POST/PUT /v1.0/bindings/ -``` - -In contrast to the previous policies, rather than just forwarding the original request content, we are going to create a brand new request based on the content of the original request and mapping it to the format expected by Dapr API. This capability comes handy when your API needs to stay the same while the backing service evolves API evolves over time. Consider the payload expected by Dapr binding API: - -```json -{ - "data": "", - "metadata": { - "": "", - "": "" - }, - "operation": "" -} -``` - -The policy will first define a `key` variable that will be generated using system guid. Once defined, that variable can be used later on in the policy. To accommodate the binding format expected by Dapr, the policy will then set `operation` attribute in APIM `invoke-dapr-binding` policy, and set `metadata` items to: - -* `source` which will be a static value indicating the record came from `APIM` -* `client-ip` which will be set to the client request IP -* `key` which will be set to the value of the variable defined above - -Finally, for `data`, we simply use the original content of the client request. - -```xml - - - - - - - APIM - @( context.Request.IpAddress ) - @( (string)context.Variables["key"] ) - - @( context.Request.Body.As() ) - - - - ... - -``` - -To apply [this policy](apim/policy-save.json) to the `save` operation on our API, submit it to the Azure management API: - -```shell -curl -i -X PUT \ - -d @apim/policy-save.json \ - -H "Content-Type: application/json" \ - -H "If-Match: *" \ - -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/operations/save/policies/policy?api-version=2019-12-01" -``` - -> Note, the support in APIM for bindings is still rolling out across Azure regions. You can safely skip this section and just demo service invocation and topic publishing if you receive an error that `invoke-dapr-binding` is not recognize. - -If everything goes well, the management API will return the created policy. Additional information about Dapr Binding support in APIM are available [here](https://aka.ms/apim/dapr/bind). - -### Gateway Configuration - -To create a self-hosted gateway which will be then deployed to the Kubernetes cluster, first, we need to create the `demo-apim-gateway` object in APIM: - -```shell -curl -i -X PUT -d '{"properties": {"description": "Dapr Gateway","locationData": {"name": "Virtual"}}}' \ - -H "Content-Type: application/json" \ - -H "If-Match: *" \ - -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/gateways/demo-apim-gateway?api-version=2019-12-01" -``` - -And then map that gateway to the previously created API: - -```shell -curl -i -X PUT -d '{ "properties": { "provisioningState": "created" } }' \ - -H "Content-Type: application/json" \ - -H "If-Match: *" \ - -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/gateways/demo-apim-gateway/apis/dapr?api-version=2019-12-01" -``` - -If everything goes well, the API returns JSON of the created objects. - -## Kubernetes - -Switching now to the Kubernetes cluster... - -### Dependencies - -To showcase the ability to expose Dapr pub/sub and binding APIs in APIM, we are going to need [Dapr components](https://github.com/dapr/docs/tree/master/concepts#components) configured on the cluster. - -> Note, while Dapr supports some 75+ different components, to keep things simple in this demo we will use Redis as both pub/sub and binding backing service - -Start with adding the Redis repo to your Helm charts: - -```shell -helm repo add bitnami https://charts.bitnami.com/bitnami -helm repo update -``` - -And install Redis and wait for the deployment to complete: - -> Note, for simplicity, we are deploying everything into the `default` namespace. - -```shell -helm install redis bitnami/redis -kubectl rollout status statefulset.apps/redis-master -kubectl rollout status statefulset.apps/redis-slave -``` - -### Dapr Components - -Dapr's modular design means that we can easily extend its functionality using [components](https://github.com/dapr/docs/tree/master/concepts#components). The specific implementation for these components which can be any number of the readily available Dapr building blocks is done in configuration. That means that it's also easy to swap or reconfigure them at runtime without the need to modify your code. - -![](img/dapr-building-blocks.png) - -To create the binding component to point to the above created Redis cluster the configuration looks like this: - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: demo-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.default.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis - key: redis-password - - name: allowedTopics - value: "messages" -``` - -Notice we are using the secret created by Redis for password so that our configuration doesn't include any secure information. We also specify that only the `messages` topic should be supported in this case. - -To apply these component configurations run: - -```shell -kubectl apply -f k8s/pubsub.yaml -kubectl apply -f k8s/binding.yaml -``` - -> Note, if you updated components after deploying the gateway you will need to restart the deployments. - -```shell -kubectl rollout restart deployment/event-subscriber -kubectl rollout status deployment/event-subscriber -kubectl rollout restart deployment/demo-apim-gateway -kubectl rollout status deployment/demo-apim-gateway -``` - -To check if the components were registered correctly in Dapr, inspect the `daprd` logs in `demo-apim-gateway` pod for `demo-events` and `demo-binding`: - -```shell -kubectl logs -l app=demo-apim-gateway -c daprd --tail=200 -``` - -### Dapr Services - -To deploy your application as a Dapr service all you need to do is augment your Kubernetes deployment template with few Dapr annotations. - -```yaml -annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "event-subscriber" - dapr.io/app-port: "8080" -``` - -> To learn more about Kubernetes sidecar configuration see [Dapr docs](https://github.com/dapr/docs/blob/master/concepts/configuration/README.md#kubernetes-sidecar-configuration). - -For this demo we will use a pre-build Docker images of two applications: [gRPC Echo Service](https://github.com/mchmarny/dapr-demos/tree/master/grpc-echo-service) and [HTTP Event Subscriber Service](https://github.com/mchmarny/dapr-demos/tree/master/http-event-subscriber). The Kubernetes deployment files for both of these are located here: - -* [k8s/echo-service.yaml](k8s/echo-service.yaml) -* [k8s/event-subscriber.yaml](k8s/event-subscriber.yaml) - -Deploy both of these and check that it is ready: - -```shell -kubectl apply -f k8s/echo-service.yaml -kubectl apply -f k8s/event-subscriber.yaml -kubectl get pods -l demo=dapr-apim -w -``` - -> Service is ready when its status is `Running` and the ready column is `2/2` (Dapr and our echo service containers both started) - -```shell -NAME READY STATUS RESTARTS AGE -echo-service-668986b998-v2ssp 2/2 Running 0 10m -event-subscriber-7d68b67d9d-5v7bf 2/2 Running 0 10m -``` - -To make sure that the event subscriber connects to the Redis service you can query the service logs - -```shell -kubectl logs -l app=event-subscriber -c daprd | grep demo-events -``` - -You should see entries containing: - -```shell -app responded with subscriptions [{demo-events messages /messages map[]}] -app is subscribed to the following topics: [messages] through pubsub=demo-events -subscribing to topic=messages on pubsub=demo-events -``` - -### Self-hosted APIM Gateway - -To connect the self-hosted gateway to APIM service, we will need to create a Kubernetes secret with the APIM gateway key. Start by getting the key which your gateway will use to connect to from APIM: - -> Note, the maximum validity for access tokens is 30 days. Update the below `expiry` parameter to be withing 30 days from today - -```shell -curl -i -X POST -d '{ "keyType": "primary", "expiry": "2020-10-10T00:00:01Z" }' \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/gateways/demo-apim-gateway/generateToken?api-version=2019-12-01" -``` - -Copy the content of `value` from the response and create a secret: - -```shell -kubectl create secret generic demo-apim-gateway-token --type Opaque --from-literal value="GatewayKey paste-the-key-here" -``` - -> Make sure the secret includes the `GatewayKey` and a space ` ` as well as the value of your token (e.g. `GatewayKey a1b2c3...`) - -Now, create a config map containing the APIM service endpoint that will be used to configure your self-hosted gateway: - -```shell -kubectl create configmap demo-apim-gateway-env --from-literal \ - "config.service.endpoint=https://${APIM_SERVICE_NAME}.management.azure-api.net/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}?api-version=2019-12-01" -``` - -And finally, deploy the gateway and check that it's ready: - -```shell -kubectl apply -f k8s/gateway.yaml -kubectl get pods -l app=demo-apim-gateway -``` - -> Note, the self-hosted gateway is deployed with 2 replicas to ensure availability during upgrades. - -Make sure both instances have status `Running` and container is ready `2/2` (gateway container + Dapr side-car). - -```shell -NAME READY STATUS RESTARTS AGE -demo-apim-gateway-6dfb968f5c-cb4t7 2/2 Running 0 26s -demo-apim-gateway-6dfb968f5c-gxrrq 2/2 Running 0 26s -``` - -To check on the gateway logs: - -```shell -kubectl logs -l app=demo-apim-gateway -c demo-apim-gateway -``` - -## Usage (API Test) - -With APIM configured and self-hosted gateway deployed we are ready to test. Start by capturing the cluster load balancer ingress IP: - -```shell -export GATEWAY_IP=$(kubectl get svc demo-apim-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -``` - -### API Subscription Key - -All of the APIs we defined in this demo are protected with subscription key. To invoke them, we will first need a subscription key: - -```shell -curl -i -H POST -d '{}' -H "Authorization: Bearer ${AZ_API_TOKEN}" \ - "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/subscriptions/master/listSecrets?api-version=2019-12-01" -``` - -The response will include both the primary and secondary keys. Copy one of them and export so we can use it for the rest of the demo: - -```shell -export AZ_API_SUB_KEY="your-api-subscription-key" -``` - -### Service Invocation - -To invoke the backing gRPC service over Dapr API exposed by APIM run: - -```shell -curl -i -X POST -d '{ "message": "hello" }' \ - -H "Content-Type: application/json" \ - -H "Ocp-Apim-Subscription-Key: ${AZ_API_SUB_KEY}" \ - -H "Ocp-Apim-Trace: true" \ - "http://${GATEWAY_IP}/echo" -``` - -If everything is configured correctly, you should see the response from your backing Dapr service: - -```json -{ "message": "hello" } -``` - -In addition, you can also check the `echo-service` logs: - -```shell -kubectl logs -l app=echo-service -c service -``` - -### Message Publishing - -To post a message to the Dapr Pub/Sub API exposed on APIM run: - -```shell -curl -i -X POST \ - -d '{ "content": "hello" }' \ - -H "Content-Type: application/json" \ - -H "Ocp-Apim-Subscription-Key: ${AZ_API_SUB_KEY}" \ - -H "Ocp-Apim-Trace: true" \ - "http://${GATEWAY_IP}/message" -``` - -If everything is configured correctly, you will see `200` status code in the header, indicating the message was successfully delivered to the Dapr API. - -You can also check the `event-subscriber` logs: - -```shell -kubectl logs -l app=event-subscriber -c service -``` - -There should be an entry similar to this: - -```shell -event - PubsubName:demo-events, Topic:messages, ID:24f0e6f0-ab29-4cd6-8617-6c6c36ac1171, Data: map[message:hello] -``` - -### Binding Invocation - -To save a record into database using the Dapr binding API exposed by APIM run: - -```shell -curl -i -X POST \ - -d '{"city":"PDX","time":"1600171062","metric":"aqi","value": 457}' \ - -H "Content-Type: application/json" \ - -H "Ocp-Apim-Subscription-Key: ${AZ_API_SUB_KEY}" \ - -H "Ocp-Apim-Trace: true" \ - "http://${GATEWAY_IP}/save" -``` - -If everything is configured correctly, you will see `200` status code in the header indicating the binding was successfully triggered on the Dapr API and our record successfully saved into the DB. - -### Debugging - -Notice in each one of our API invocations we have been including the `Ocp-Apim-Trace: true` header parameter. APIM provides an ability to trace requests across the policy execution chain which is helps in debugging your policy. The response of each one fo the above invocation includes the `Ocp-Apim-Trace-Location` header parameter. Just paste the value of that parameter into your browser to see the full trace stack in JSON. The trace can get pretty long so here are few Dapr-specific snippets: - - -```json -... -{ - "source": "request-forwarder", - "timestamp": "2020-09-11T11:15:52.9405903Z", - "elapsed": "00:00:00.1382166", - "data": { - "message": "Request is being forwarded to the backend service. Timeout set to 300 seconds", - "request": { - "method": "POST", - "url": "http://localhost:3500/v1.0/publish/demo-events/messages" - } - } -}, -{ - "source": "publish-to-dapr", - "timestamp": "2020-09-11T11:15:53.1899121Z", - "elapsed": "00:00:00.3875400", - "data": { - "response": { - "status": { - "code": 200, - "reason": "OK" - }, - "headers": [ - { - "name": "Server", - "value": "fasthttp" - }, - { - "name": "Date", - "value": "Fri, 11 Sep 2020 11:15:52 GMT" - }, - { - "name": "Content-Length", - "value": "0" - }, - { - "name": "Traceparent", - "value": "00-5b1f0bdfc2191742a4635a906359a7aa-196f5df2e977b00a-01" - } - ] - } - } -} -... -``` - -## Summary - -This demo illustrated how to setup the APIM service and deploy the self-hosted gateway into your cluster. Using this gateway you can mange access to any number of Dapr services hosted on Kubernetes. You can find out more about all the features of APIM (e.g. Discovery, Caching, Logging etc.) [here](https://azure.microsoft.com/en-us/services/api-management/). - -## Cleanup - -```shell -kubectl delete -f k8s/gateway.yaml -kubectl delete secret demo-apim-gateway-token -kubectl delete configmap demo-apim-gateway-env - -kubectl delete -f k8s/echo-service.yaml -kubectl delete -f k8s/event-subscriber.yaml - -kubectl delete -f k8s/pubsub.yaml -kubectl delete -f k8s/binding.yaml - -az apim delete --name $APIM_SERVICE_NAME --no-wait --yes -``` - +# Dapr & Azure API Management Integration Demo + +Dapr integration with [Azure API Management](https://azure.microsoft.com/en-us/services/api-management/) (APIM) using self-hosted gateway on Kubernetes. + +![APIM Self-hosted Gateway Overview](img/overview-diagram.png) + +In this demo we will walk through the configuration of API Management service and its self-hosted gateway on Kubernetes. To illustrate the Dapr integration we will also review three Dapr use-cases: + +* Invocation of a specific Dapr service method +* Publishing content to a Pub/Sub topic +* Binding invocation with request content transformation + +In addition, we will overview the use of APIM tracing to debug your configuration. + +> While you can accomplish everything we show here in Azure portal, to make this demo easier to reliably reproduce, we will be using only the Azure CLI and APIs. + +## Prerequisite + +* [Azure account](https://azure.microsoft.com/en-us/free/) +* [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) +* [Kubernetes cluster with Dapr](https://github.com/dapr/docs/blob/v0.9.0/getting-started/environment-setup.md#installing-dapr-on-a-kubernetes-cluster) +* [Helm](https://helm.sh/docs/intro/install/) + +## Terminology + +You will see a few different APIs being used throughout this demo. At one point we are even going to use one API to manage another (API inception?). Here is short summary to help you keep all these APIs straight. Hope it helps: + +* [Azure API](https://docs.microsoft.com/en-us/rest/api/apimanagement/) - this is the API provided by Azure to manage its service (yes, including the API Management Service) +* [API in APIM](https://docs.microsoft.com/en-us/azure/api-management/edit-api) - is the API which we will define in APIM service. Its operations will be used by the users +* [Dapr API](https://github.com/dapr/docs/tree/master/reference/api#dapr-api-reference) - are the RESTful HTTP APIs defined by Dapr which developers interact with building applications + +## Setup + +To make this demo easier to reproduce, start by exporting the name for your new Azure API Management (APIM) service. + +> Note, the name of your API Management service instance has to be globally unique! + +```shell +export APIM_SERVICE_NAME="dapr-apim-demo" +``` + +In addition also export the Azure [Subscription ID](https://docs.bitnami.com/azure/faq/administration/find-subscription-id/) and [Resource Group](https://docs.bitnami.com/azure/faq/administration/find-deployment-resourcegroup-id/) where you want to create that service. + +```shell +export AZ_SUBSCRIPTION_ID="your-subscription-id" +export AZ_RESOURCE_GROUP="your-resource-group" +``` + +## Azure API Management + +We will start by configuring the Azure API Management service. + +### Service Creation + +Create service instance: + +> The `publisher-email` and `publisher-name` are only required to receive system notifications e-mails. + +```shell +az apim create --name $APIM_SERVICE_NAME \ + --subscription $AZ_SUBSCRIPTION_ID \ + --resource-group $AZ_RESOURCE_GROUP \ + --publisher-email "you@your-domain.com" \ + --publisher-name "Your Name" +``` + +> Note, depending on the SKU and resource group configuration, this operation may take 15+ min. While this running, consider quick read on [API Management Concepts](https://docs.microsoft.com/en-us/azure/api-management/api-management-key-concepts#-apis-and-operations) + +### API Configuration + +Each [API operation](https://docs.microsoft.com/en-us/azure/api-management/api-management-key-concepts#-apis-and-operations) defined in APIM will map to one Dapr API. To define these mappings you will use OpenAPI format defined in [apim/api.yaml](./apim/api.yaml) file. You will need to update the OpenAPI file with the name of the APIM service created above: + +```yaml +servers: + - url: http://.azure-api.net + - url: https://.azure-api.net +``` + +When finished, import that OpenAPI definition fle into APIM service instance: + +```shell +az apim api import --path / \ + --api-id dapr \ + --subscription $AZ_SUBSCRIPTION_ID \ + --resource-group $AZ_RESOURCE_GROUP \ + --service-name $APIM_SERVICE_NAME \ + --display-name "Demo Dapr Service API" \ + --protocols http https \ + --subscription-required true \ + --specification-path apim/api.yaml \ + --specification-format OpenApi +``` + +> Notice the `subscription-required` parameter is set to `true` which means that all invocations against the `dapr` API will need a subscription key. We cover how to obtain the subscription key later. + +### Azure API Token + +Export the Azure management API token to use through this demo. + +```shell +export AZ_API_TOKEN=$(az account get-access-token --resource=https://management.azure.com --query accessToken --output tsv) +``` + +> If you receive an error later that your token expired, just re-run this command + +### Policy Management + +APIM [Policies](https://docs.microsoft.com/en-us/azure/api-management/api-management-key-concepts#--policies) are sequentially executed on each request. We will start by defining "global" policy to throttle all operation invocations on our API, then add individual policies for each operation to add specific options. + +#### Global Policy + +APIM policies are defined inside of inbound, outbound, and backend elements. In our case to apply policy that will rate-limit all requests on all operations (before they are forwarded to Dapr API), we will place the global policy within the `inbound` section. + +> Note, the rate limit quota we defined here is being shared across all the replicas of self-hosted gateway. In default configuration, where there are 2 replicas, this policy would actually be half of the permitted calls per minute. + +```xml + + + + + ... + +``` + +Apply that [policy](apim/policy-all.json) to all operations submit it to the Azure management API. + +```shell +curl -i -X PUT \ + -d @apim/policy-all.json \ + -H "Content-Type: application/json" \ + -H "If-Match: *" \ + -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/policies/policy?api-version=2019-12-01" +``` + +If everything goes well, the management API will return the created policy. + +#### Echo Service Policy + +The Dapr service invocation handles all the service discovery, so to invoke a specific method on any Dapr service users follow this API: + +```http +POST/GET/PUT/DELETE /v1.0/invoke//method/ +``` + +To enable users to invoke the `echo` method on Dapr service with ID of `echo-service` we will create a policy that inherits the global policy (``) first, to ensure only authorize service invocation are passed to the backend Dapr API. Then to "map" the invocation we set `dapr` as the "backend-id" and define the Dapr service and method attributes to specific service ID and method name. + +```xml + + + + + + ... + +``` + +To apply [this policy](apim/policy-echo.json) to the `echo` operation on our API, submit it to the Azure management API: + +```shell +curl -i -X PUT \ + -d @apim/policy-echo.json \ + -H "Content-Type: application/json" \ + -H "If-Match: *" \ + -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/operations/echo/policies/policy?api-version=2019-12-01" +``` + +If everything goes well, the management API will return the created policy. Additional information about Dapr Service Invocation in APIM are available [here](https://aka.ms/apim/dapr/invoke). + +Also, since the external mapping of the API user invocations to Dapr is done in APIM policy, it can be easily re-mapped to any other version as the API implementation evolves over time. + +![](img/backend-policy.png) + +#### Message Topic Policy + +In addition to Dapr service invocation, APIM can also be used to publish to Dapr Pub/Sub API: + +```http +POST /v1.0/publish// +``` + +To expose the `messages` topic configured in the `demo-events` component we will start by inheriting the global policy like before, and then set the publish policy to format the request that will be passed to the Dapr Pub/Sub API: + +```xml + + + + @(context.Request.Body.As()) + + + ... + +``` + +To apply [this policy](apim/policy-message.json) to the `message` operation on our API, submit it to the Azure management API: + + +```shell +curl -i -X PUT \ + -d @apim/policy-message.json \ + -H "Content-Type: application/json" \ + -H "If-Match: *" \ + -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/operations/message/policies/policy?api-version=2019-12-01" +``` + +If everything goes well, the management API will return the created policy. Additional information about Dapr Pub/Sub support in APIM are available [here](https://aka.ms/apim/dapr/pubsub). + +#### Save Binding Policy + +In our final case, we are going to overview exposing the Dapr binding API. + +```http +POST/PUT /v1.0/bindings/ +``` + +In contrast to the previous policies, rather than just forwarding the original request content, we are going to create a brand new request based on the content of the original request and mapping it to the format expected by Dapr API. This capability comes handy when your API needs to stay the same while the backing service evolves API evolves over time. Consider the payload expected by Dapr binding API: + +```json +{ + "data": "", + "metadata": { + "": "", + "": "" + }, + "operation": "" +} +``` + +The policy will first define a `key` variable that will be generated using system guid. Once defined, that variable can be used later on in the policy. To accommodate the binding format expected by Dapr, the policy will then set `operation` attribute in APIM `invoke-dapr-binding` policy, and set `metadata` items to: + +* `source` which will be a static value indicating the record came from `APIM` +* `client-ip` which will be set to the client request IP +* `key` which will be set to the value of the variable defined above + +Finally, for `data`, we simply use the original content of the client request. + +```xml + + + + + + + APIM + @( context.Request.IpAddress ) + @( (string)context.Variables["key"] ) + + @( context.Request.Body.As() ) + + + + ... + +``` + +To apply [this policy](apim/policy-save.json) to the `save` operation on our API, submit it to the Azure management API: + +```shell +curl -i -X PUT \ + -d @apim/policy-save.json \ + -H "Content-Type: application/json" \ + -H "If-Match: *" \ + -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/apis/dapr/operations/save/policies/policy?api-version=2019-12-01" +``` + +> Note, the support in APIM for bindings is still rolling out across Azure regions. You can safely skip this section and just demo service invocation and topic publishing if you receive an error that `invoke-dapr-binding` is not recognize. + +If everything goes well, the management API will return the created policy. Additional information about Dapr Binding support in APIM are available [here](https://aka.ms/apim/dapr/bind). + +### Gateway Configuration + +To create a self-hosted gateway which will be then deployed to the Kubernetes cluster, first, we need to create the `demo-apim-gateway` object in APIM: + +```shell +curl -i -X PUT -d '{"properties": {"description": "Dapr Gateway","locationData": {"name": "Virtual"}}}' \ + -H "Content-Type: application/json" \ + -H "If-Match: *" \ + -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/gateways/demo-apim-gateway?api-version=2019-12-01" +``` + +And then map that gateway to the previously created API: + +```shell +curl -i -X PUT -d '{ "properties": { "provisioningState": "created" } }' \ + -H "Content-Type: application/json" \ + -H "If-Match: *" \ + -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/gateways/demo-apim-gateway/apis/dapr?api-version=2019-12-01" +``` + +If everything goes well, the API returns JSON of the created objects. + +## Kubernetes + +Switching now to the Kubernetes cluster... + +### Dependencies + +To showcase the ability to expose Dapr pub/sub and binding APIs in APIM, we are going to need [Dapr components](https://github.com/dapr/docs/tree/master/concepts#components) configured on the cluster. + +> Note, while Dapr supports some 75+ different components, to keep things simple in this demo we will use Redis as both pub/sub and binding backing service + +Start with adding the Redis repo to your Helm charts: + +```shell +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo update +``` + +And install Redis and wait for the deployment to complete: + +> Note, for simplicity, we are deploying everything into the `default` namespace. + +```shell +helm install redis bitnami/redis +kubectl rollout status statefulset.apps/redis-master +kubectl rollout status statefulset.apps/redis-slave +``` + +### Dapr Components + +Dapr's modular design means that we can easily extend its functionality using [components](https://github.com/dapr/docs/tree/master/concepts#components). The specific implementation for these components which can be any number of the readily available Dapr building blocks is done in configuration. That means that it's also easy to swap or reconfigure them at runtime without the need to modify your code. + +![](img/dapr-building-blocks.png) + +To create the binding component to point to the above created Redis cluster the configuration looks like this: + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: demo-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.default.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis + key: redis-password + - name: allowedTopics + value: "messages" +``` + +Notice we are using the secret created by Redis for password so that our configuration doesn't include any secure information. We also specify that only the `messages` topic should be supported in this case. + +To apply these component configurations run: + +```shell +kubectl apply -f k8s/pubsub.yaml +kubectl apply -f k8s/binding.yaml +``` + +> Note, if you updated components after deploying the gateway you will need to restart the deployments. + +```shell +kubectl rollout restart deployment/event-subscriber +kubectl rollout status deployment/event-subscriber +kubectl rollout restart deployment/demo-apim-gateway +kubectl rollout status deployment/demo-apim-gateway +``` + +To check if the components were registered correctly in Dapr, inspect the `daprd` logs in `demo-apim-gateway` pod for `demo-events` and `demo-binding`: + +```shell +kubectl logs -l app=demo-apim-gateway -c daprd --tail=200 +``` + +### Dapr Services + +To deploy your application as a Dapr service all you need to do is augment your Kubernetes deployment template with few Dapr annotations. + +```yaml +annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "event-subscriber" + dapr.io/app-port: "8080" +``` + +> To learn more about Kubernetes sidecar configuration see [Dapr docs](https://github.com/dapr/docs/blob/master/concepts/configuration/README.md#kubernetes-sidecar-configuration). + +For this demo we will use a pre-build Docker images of two applications: [gRPC Echo Service](https://github.com/mchmarny/dapr-demos/tree/master/grpc-echo-service) and [HTTP Event Subscriber Service](https://github.com/mchmarny/dapr-demos/tree/master/http-event-subscriber). The Kubernetes deployment files for both of these are located here: + +* [k8s/echo-service.yaml](k8s/echo-service.yaml) +* [k8s/event-subscriber.yaml](k8s/event-subscriber.yaml) + +Deploy both of these and check that it is ready: + +```shell +kubectl apply -f k8s/echo-service.yaml +kubectl apply -f k8s/event-subscriber.yaml +kubectl get pods -l demo=dapr-apim -w +``` + +> Service is ready when its status is `Running` and the ready column is `2/2` (Dapr and our echo service containers both started) + +```shell +NAME READY STATUS RESTARTS AGE +echo-service-668986b998-v2ssp 2/2 Running 0 10m +event-subscriber-7d68b67d9d-5v7bf 2/2 Running 0 10m +``` + +To make sure that the event subscriber connects to the Redis service you can query the service logs + +```shell +kubectl logs -l app=event-subscriber -c daprd | grep demo-events +``` + +You should see entries containing: + +```shell +app responded with subscriptions [{demo-events messages /messages map[]}] +app is subscribed to the following topics: [messages] through pubsub=demo-events +subscribing to topic=messages on pubsub=demo-events +``` + +### Self-hosted APIM Gateway + +To connect the self-hosted gateway to APIM service, we will need to create a Kubernetes secret with the APIM gateway key. Start by getting the key which your gateway will use to connect to from APIM: + +> Note, the maximum validity for access tokens is 30 days. Update the below `expiry` parameter to be withing 30 days from today + +```shell +curl -i -X POST -d '{ "keyType": "primary", "expiry": "2020-10-10T00:00:01Z" }' \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/gateways/demo-apim-gateway/generateToken?api-version=2019-12-01" +``` + +Copy the content of `value` from the response and create a secret: + +```shell +kubectl create secret generic demo-apim-gateway-token --type Opaque --from-literal value="GatewayKey paste-the-key-here" +``` + +> Make sure the secret includes the `GatewayKey` and a space ` ` as well as the value of your token (e.g. `GatewayKey a1b2c3...`) + +Now, create a config map containing the APIM service endpoint that will be used to configure your self-hosted gateway: + +```shell +kubectl create configmap demo-apim-gateway-env --from-literal \ + "config.service.endpoint=https://${APIM_SERVICE_NAME}.management.azure-api.net/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}?api-version=2019-12-01" +``` + +And finally, deploy the gateway and check that it's ready: + +```shell +kubectl apply -f k8s/gateway.yaml +kubectl get pods -l app=demo-apim-gateway +``` + +> Note, the self-hosted gateway is deployed with 2 replicas to ensure availability during upgrades. + +Make sure both instances have status `Running` and container is ready `2/2` (gateway container + Dapr side-car). + +```shell +NAME READY STATUS RESTARTS AGE +demo-apim-gateway-6dfb968f5c-cb4t7 2/2 Running 0 26s +demo-apim-gateway-6dfb968f5c-gxrrq 2/2 Running 0 26s +``` + +To check on the gateway logs: + +```shell +kubectl logs -l app=demo-apim-gateway -c demo-apim-gateway +``` + +## Usage (API Test) + +With APIM configured and self-hosted gateway deployed we are ready to test. Start by capturing the cluster load balancer ingress IP: + +```shell +export GATEWAY_IP=$(kubectl get svc demo-apim-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +``` + +### API Subscription Key + +All of the APIs we defined in this demo are protected with subscription key. To invoke them, we will first need a subscription key: + +```shell +curl -i -H POST -d '{}' -H "Authorization: Bearer ${AZ_API_TOKEN}" \ + "https://management.azure.com/subscriptions/${AZ_SUBSCRIPTION_ID}/resourceGroups/${AZ_RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_SERVICE_NAME}/subscriptions/master/listSecrets?api-version=2019-12-01" +``` + +The response will include both the primary and secondary keys. Copy one of them and export so we can use it for the rest of the demo: + +```shell +export AZ_API_SUB_KEY="your-api-subscription-key" +``` + +### Service Invocation + +To invoke the backing gRPC service over Dapr API exposed by APIM run: + +```shell +curl -i -X POST -d '{ "message": "hello" }' \ + -H "Content-Type: application/json" \ + -H "Ocp-Apim-Subscription-Key: ${AZ_API_SUB_KEY}" \ + -H "Ocp-Apim-Trace: true" \ + "http://${GATEWAY_IP}/echo" +``` + +If everything is configured correctly, you should see the response from your backing Dapr service: + +```json +{ "message": "hello" } +``` + +In addition, you can also check the `echo-service` logs: + +```shell +kubectl logs -l app=echo-service -c service +``` + +### Message Publishing + +To post a message to the Dapr Pub/Sub API exposed on APIM run: + +```shell +curl -i -X POST \ + -d '{ "content": "hello" }' \ + -H "Content-Type: application/json" \ + -H "Ocp-Apim-Subscription-Key: ${AZ_API_SUB_KEY}" \ + -H "Ocp-Apim-Trace: true" \ + "http://${GATEWAY_IP}/message" +``` + +If everything is configured correctly, you will see `200` status code in the header, indicating the message was successfully delivered to the Dapr API. + +You can also check the `event-subscriber` logs: + +```shell +kubectl logs -l app=event-subscriber -c service +``` + +There should be an entry similar to this: + +```shell +event - PubsubName:demo-events, Topic:messages, ID:24f0e6f0-ab29-4cd6-8617-6c6c36ac1171, Data: map[message:hello] +``` + +### Binding Invocation + +To save a record into database using the Dapr binding API exposed by APIM run: + +```shell +curl -i -X POST \ + -d '{"city":"PDX","time":"1600171062","metric":"aqi","value": 457}' \ + -H "Content-Type: application/json" \ + -H "Ocp-Apim-Subscription-Key: ${AZ_API_SUB_KEY}" \ + -H "Ocp-Apim-Trace: true" \ + "http://${GATEWAY_IP}/save" +``` + +If everything is configured correctly, you will see `200` status code in the header indicating the binding was successfully triggered on the Dapr API and our record successfully saved into the DB. + +### Debugging + +Notice in each one of our API invocations we have been including the `Ocp-Apim-Trace: true` header parameter. APIM provides an ability to trace requests across the policy execution chain which is helps in debugging your policy. The response of each one fo the above invocation includes the `Ocp-Apim-Trace-Location` header parameter. Just paste the value of that parameter into your browser to see the full trace stack in JSON. The trace can get pretty long so here are few Dapr-specific snippets: + + +```json +... +{ + "source": "request-forwarder", + "timestamp": "2020-09-11T11:15:52.9405903Z", + "elapsed": "00:00:00.1382166", + "data": { + "message": "Request is being forwarded to the backend service. Timeout set to 300 seconds", + "request": { + "method": "POST", + "url": "http://localhost:3500/v1.0/publish/demo-events/messages" + } + } +}, +{ + "source": "publish-to-dapr", + "timestamp": "2020-09-11T11:15:53.1899121Z", + "elapsed": "00:00:00.3875400", + "data": { + "response": { + "status": { + "code": 200, + "reason": "OK" + }, + "headers": [ + { + "name": "Server", + "value": "fasthttp" + }, + { + "name": "Date", + "value": "Fri, 11 Sep 2020 11:15:52 GMT" + }, + { + "name": "Content-Length", + "value": "0" + }, + { + "name": "Traceparent", + "value": "00-5b1f0bdfc2191742a4635a906359a7aa-196f5df2e977b00a-01" + } + ] + } + } +} +... +``` + +## Summary + +This demo illustrated how to setup the APIM service and deploy the self-hosted gateway into your cluster. Using this gateway you can mange access to any number of Dapr services hosted on Kubernetes. You can find out more about all the features of APIM (e.g. Discovery, Caching, Logging etc.) [here](https://azure.microsoft.com/en-us/services/api-management/). + +## Cleanup + +```shell +kubectl delete -f k8s/gateway.yaml +kubectl delete secret demo-apim-gateway-token +kubectl delete configmap demo-apim-gateway-env + +kubectl delete -f k8s/echo-service.yaml +kubectl delete -f k8s/event-subscriber.yaml + +kubectl delete -f k8s/pubsub.yaml +kubectl delete -f k8s/binding.yaml + +az apim delete --name $APIM_SERVICE_NAME --no-wait --yes +``` + diff --git a/apim-gateway/apim/api.yaml b/apim-gateway/apim/api.yaml index 896a006..ed4a5a9 100644 --- a/apim-gateway/apim/api.yaml +++ b/apim-gateway/apim/api.yaml @@ -1,50 +1,50 @@ -openapi: 3.0.1 -info: - title: dapr - version: '1.0' -servers: - - url: http://dapr-apim-demo.azure-api.net - - url: https://dapr-apim-demo.azure-api.net -paths: - /echo: - post: - summary: Echo Service - description: Invoke service using Dapr API - operationId: echo - requestBody: - content: - application/json: - example: - message: hello - responses: - '200': - description: '' - /message: - post: - summary: Message Topic - description: Post to topic using Dapr API - operationId: message - requestBody: - content: - application/json: - example: - content: hello - responses: - '200': - description: '' - /save: - post: - summary: DB Binding - description: DB binding using Dapr API - operationId: save - requestBody: - content: - application/json: - example: - city: PDX, - time: 1600171062 - metric: aqi - value: 457 - responses: - '200': +openapi: 3.0.1 +info: + title: dapr + version: '1.0' +servers: + - url: http://dapr-apim-demo.azure-api.net + - url: https://dapr-apim-demo.azure-api.net +paths: + /echo: + post: + summary: Echo Service + description: Invoke service using Dapr API + operationId: echo + requestBody: + content: + application/json: + example: + message: hello + responses: + '200': + description: '' + /message: + post: + summary: Message Topic + description: Post to topic using Dapr API + operationId: message + requestBody: + content: + application/json: + example: + content: hello + responses: + '200': + description: '' + /save: + post: + summary: DB Binding + description: DB binding using Dapr API + operationId: save + requestBody: + content: + application/json: + example: + city: PDX, + time: 1600171062 + metric: aqi + value: 457 + responses: + '200': description: '' \ No newline at end of file diff --git a/apim-gateway/apim/policy-all.json b/apim-gateway/apim/policy-all.json index 4b509aa..c9554aa 100644 --- a/apim-gateway/apim/policy-all.json +++ b/apim-gateway/apim/policy-all.json @@ -1,6 +1,6 @@ -{ - "properties": { - "format": "xml", - "value": "" - } +{ + "properties": { + "format": "xml", + "value": "" + } } \ No newline at end of file diff --git a/apim-gateway/apim/policy-echo.json b/apim-gateway/apim/policy-echo.json index b68c52f..8fefc84 100644 --- a/apim-gateway/apim/policy-echo.json +++ b/apim-gateway/apim/policy-echo.json @@ -1,6 +1,6 @@ -{ - "properties": { - "format": "xml", - "value": "" - } +{ + "properties": { + "format": "xml", + "value": "" + } } \ No newline at end of file diff --git a/apim-gateway/apim/policy-message.json b/apim-gateway/apim/policy-message.json index 3aaf713..5117787 100644 --- a/apim-gateway/apim/policy-message.json +++ b/apim-gateway/apim/policy-message.json @@ -1,6 +1,6 @@ -{ - "properties": { - "format": "xml", - "value": "@( context.Request.Body.As<string>() )" - } +{ + "properties": { + "format": "xml", + "value": "@( context.Request.Body.As<string>() )" + } } \ No newline at end of file diff --git a/apim-gateway/apim/policy-save.json b/apim-gateway/apim/policy-save.json index 8644a16..d17c586 100644 --- a/apim-gateway/apim/policy-save.json +++ b/apim-gateway/apim/policy-save.json @@ -1,6 +1,6 @@ -{ - "properties": { - "format": "xml", - "value": "APIM@( context.Request.IpAddress )@( (string)context.Variables[\"key\"] )@( context.Request.Body.As<string>() )" - } +{ + "properties": { + "format": "xml", + "value": "APIM@( context.Request.IpAddress )@( (string)context.Variables[\"key\"] )@( context.Request.Body.As<string>() )" + } } \ No newline at end of file diff --git a/apim-gateway/k8s/binding.yaml b/apim-gateway/k8s/binding.yaml index a5b5e0f..ae2261f 100644 --- a/apim-gateway/k8s/binding.yaml +++ b/apim-gateway/k8s/binding.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: demo-binding -spec: - type: bindings.redis - metadata: - - name: redisHost - value: redis-master.default.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: demo-binding +spec: + type: bindings.redis + metadata: + - name: redisHost + value: redis-master.default.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis key: redis-password \ No newline at end of file diff --git a/apim-gateway/k8s/echo-service.yaml b/apim-gateway/k8s/echo-service.yaml index 7a42915..9e3d4dd 100644 --- a/apim-gateway/k8s/echo-service.yaml +++ b/apim-gateway/k8s/echo-service.yaml @@ -1,35 +1,35 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: echo-service - labels: - app: echo-service - demo: dapr-apim -spec: - replicas: 1 - selector: - matchLabels: - app: echo-service - template: - metadata: - labels: - app: echo-service - demo: dapr-apim - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "echo-service" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-echo-service:v0.11.1 - imagePullPolicy: Always - ports: - - containerPort: 60002 - env: - - name: ADDRESS +apiVersion: apps/v1 +kind: Deployment +metadata: + name: echo-service + labels: + app: echo-service + demo: dapr-apim +spec: + replicas: 1 + selector: + matchLabels: + app: echo-service + template: + metadata: + labels: + app: echo-service + demo: dapr-apim + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "echo-service" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-echo-service:v0.11.1 + imagePullPolicy: Always + ports: + - containerPort: 60002 + env: + - name: ADDRESS value: ":60002" \ No newline at end of file diff --git a/apim-gateway/k8s/event-subscriber.yaml b/apim-gateway/k8s/event-subscriber.yaml index 449a1fb..fd4794e 100644 --- a/apim-gateway/k8s/event-subscriber.yaml +++ b/apim-gateway/k8s/event-subscriber.yaml @@ -1,36 +1,36 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: event-subscriber - labels: - app: event-subscriber - demo: dapr-apim -spec: - selector: - matchLabels: - app: event-subscriber - template: - metadata: - labels: - app: event-subscriber - demo: dapr-apim - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "event-subscriber" - dapr.io/app-port: "8080" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/http-event-subscriber:v0.11.1 - ports: - - containerPort: 8080 - env: - - name: PORT - value: "8080" - - name: PUBSUB_NAME - value: "demo-events" - - name: TOPIC_NAME +apiVersion: apps/v1 +kind: Deployment +metadata: + name: event-subscriber + labels: + app: event-subscriber + demo: dapr-apim +spec: + selector: + matchLabels: + app: event-subscriber + template: + metadata: + labels: + app: event-subscriber + demo: dapr-apim + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "event-subscriber" + dapr.io/app-port: "8080" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/http-event-subscriber:v0.11.1 + ports: + - containerPort: 8080 + env: + - name: PORT + value: "8080" + - name: PUBSUB_NAME + value: "demo-events" + - name: TOPIC_NAME value: "messages" \ No newline at end of file diff --git a/apim-gateway/k8s/gateway.yaml b/apim-gateway/k8s/gateway.yaml index 9f13509..1f0722b 100644 --- a/apim-gateway/k8s/gateway.yaml +++ b/apim-gateway/k8s/gateway.yaml @@ -1,71 +1,71 @@ -# NOTE: Before deploying to a production environment, please review the documentation -> https://aka.ms/self-hosted-gateway-production ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: demo-apim-gateway -spec: - replicas: 2 - selector: - matchLabels: - app: demo-apim-gateway - strategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 0 - maxSurge: 25% - template: - metadata: - labels: - app: demo-apim-gateway - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "demo-apim-gateway" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - terminationGracePeriodSeconds: 60 - containers: - - name: demo-apim-gateway - image: mcr.microsoft.com/azure-api-management/gateway:beta - ports: - - name: http - containerPort: 8080 - - name: https - containerPort: 8081 - readinessProbe: - httpGet: - path: /internal-status-0123456789abcdef - port: http - scheme: HTTP - initialDelaySeconds: 0 - periodSeconds: 5 - failureThreshold: 3 - successThreshold: 1 - env: - - name: config.service.auth - valueFrom: - secretKeyRef: - name: demo-apim-gateway-token - key: value - envFrom: - - configMapRef: - name: demo-apim-gateway-env ---- -apiVersion: v1 -kind: Service -metadata: - name: demo-apim-gateway -spec: - type: LoadBalancer - externalTrafficPolicy: Local - ports: - - name: http - port: 80 - targetPort: 8080 - - name: https - port: 443 - targetPort: 8081 - selector: +# NOTE: Before deploying to a production environment, please review the documentation -> https://aka.ms/self-hosted-gateway-production +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: demo-apim-gateway +spec: + replicas: 2 + selector: + matchLabels: + app: demo-apim-gateway + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 25% + template: + metadata: + labels: + app: demo-apim-gateway + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "demo-apim-gateway" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + terminationGracePeriodSeconds: 60 + containers: + - name: demo-apim-gateway + image: mcr.microsoft.com/azure-api-management/gateway:beta + ports: + - name: http + containerPort: 8080 + - name: https + containerPort: 8081 + readinessProbe: + httpGet: + path: /internal-status-0123456789abcdef + port: http + scheme: HTTP + initialDelaySeconds: 0 + periodSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + env: + - name: config.service.auth + valueFrom: + secretKeyRef: + name: demo-apim-gateway-token + key: value + envFrom: + - configMapRef: + name: demo-apim-gateway-env +--- +apiVersion: v1 +kind: Service +metadata: + name: demo-apim-gateway +spec: + type: LoadBalancer + externalTrafficPolicy: Local + ports: + - name: http + port: 80 + targetPort: 8080 + - name: https + port: 443 + targetPort: 8081 + selector: app: demo-apim-gateway \ No newline at end of file diff --git a/apim-gateway/k8s/pubsub.yaml b/apim-gateway/k8s/pubsub.yaml index 1bb17bd..1f85bba 100644 --- a/apim-gateway/k8s/pubsub.yaml +++ b/apim-gateway/k8s/pubsub.yaml @@ -1,15 +1,15 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: demo-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.default.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis - key: redis-password - - name: allowedTopics +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: demo-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.default.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis + key: redis-password + - name: allowedTopics value: "messages" \ No newline at end of file diff --git a/autoscaling-on-queue/README.md b/autoscaling-on-queue/README.md index 7868cfb..a8d02fa 100644 --- a/autoscaling-on-queue/README.md +++ b/autoscaling-on-queue/README.md @@ -1,205 +1,205 @@ -# Autoscaling Dapr service based on queue depth - -Dapr, with its modular building-block approach, along with the 10+ different Pub/Sub components, makes it easy to write message processing microservices. Since Dapr runs in VM, on bare-metal, in the Cloud, or even on the Edge... the autoscaling Dapr services is left to the hosting later. - -In case of Kubernetes, Dapr integrates with [Keda](https://github.com/kedacore/keda), an event driven autoscaler for Kubernetes. In this demo I'll walk through the configuration of Dapr microservice to scale along with the back pressure on [Kafka](https://kafka.apache.org) queue that service processes. - -![](image/diagram.png) - -## Setup - -The autoscaling demo requires [Dapr](https://dapr.io). If you don't already have a Kubernetes cluster with Dapr installed you can use the included [setup](../setup) to configure all the dependencies. - -### Keda - -Start by install [Keda](https://github.com/kedacore/keda) into the cluster and wait for it become ready: - -```shell -kubectl apply -f deployment/keda-2.0.0-beta.yaml -kubectl rollout status deployment.apps/keda-operator -n keda -``` - -### Kafka (optional) - -Next, if you don't have access to Kafka you can use these instructions to install Kafka into the cluster: - -```shell -helm repo add confluentinc https://confluentinc.github.io/cp-helm-charts/ -helm repo update -kubectl create ns kafka -helm install kafka confluentinc/cp-helm-charts -n kafka \ - --set cp-schema-registry.enabled=false \ - --set cp-kafka-rest.enabled=false \ - --set cp-kafka-connect.enabled=false \ - --set dataLogDirStorageClass=default \ - --set dataDirStorageClass=default \ - --set storageClass=default -kubectl rollout status deployment.apps/kafka-cp-control-center -n kafka -kubectl rollout status deployment.apps/kafka-cp-ksql-server -n kafka -kubectl rollout status statefulset.apps/kafka-cp-kafka -n kafka -kubectl rollout status statefulset.apps/kafka-cp-zookeeper -n kafka -``` - -When done, also deploy Kafka client and wait until it's ready: - -```shell -kubectl apply -n kafka -f deployment/kafka-client.yaml -kubectl wait -n kafka --for=condition=ready pod kafka-client --timeout=120s -``` - -Next, create the `metric` topic which we will use in this demo: - -> The number of `partitions` is connected to the maximum number of replicas Keda will create. - -```shell -kubectl -n kafka exec -it kafka-client -- kafka-topics \ - --zookeeper kafka-cp-zookeeper-headless:2181 \ - --topic metric \ - --create \ - --partitions 10 \ - --replication-factor 3 \ - --if-not-exists -``` - -## Deployment - -To configure the autoscaling demo we will deploy two deployments: `subscriber` which will be used to process messages of the `metric` queue in Kafka, and the `producer`, which will be publishing messages. To make the `producer` compatible with any one of the Pub/Sub components supported by Dapr we will publish the events onto the Kafka queue using Dapr APIs. - -### Subscriber - -The `subscriber` service doesn't really do anything with the messages. To resemble real-life processing which may take some time to process messages, the `subscriber` allows for explicit processing time setting. The default value is `300ms`. We will go over how to modify that later. - -To deploy the `subscriber` service, apply the [Kafka Dapr component](deployment/kafka-pubsub.yaml), the [message subscriber service](deployment/subscriber.yaml), and the [subscriber service Keda scaler](subscriber-scaler.yaml): - -```shell -kubectl apply -f deployment/kafka-pubsub.yaml -kubectl apply -f deployment/subscriber.yaml -kubectl apply -f deployment/subscriber-scaler.yaml -``` - -When done, start watching for the number of replicas of the deployed `subscriber` service: - -```shell -watch kubectl get pods -l app=autoscaling-subscriber -``` - -> Note, by default the subscriber service Keda scaler is set to scale to 0, so you will not see any pods yet. We will publish data on the `metric` topic with the `producer`. - -### Producer - -In a second terminal session, deploy the [producer service](deployment/producer.yaml) and wait for it to be ready: - -```shell -kubectl apply -f deployment/producer.yaml -kubectl rollout status deployment/autoscaling-producer -``` - -## Demo - -Back in the initial terminal now, some 20-30 seconds after the `producer` starts, you should see the number of `subscriber` pods being adjusted by Keda based on the number of the `metric` topic: - -```shell -NAME READY STATUS RESTARTS AGE -autoscaling-subscriber-696ffb5c7b-64zqq 2/2 Running 0 31s -autoscaling-subscriber-696ffb5c7b-67f74 2/2 Running 0 15s -autoscaling-subscriber-696ffb5c7b-gpc2d 2/2 Running 0 7m42s -``` - -By default the `subscriber-scaler` is set to scale-to-zero and has the polling frequency of `15s`. You can adjust these values in [deployment/subscriber-scaler.yaml](deployment/subscriber-scaler.yaml): - -```yaml -pollingInterval: 15 -minReplicaCount: 0 -maxReplicaCount: 10 -cooldownPeriod: 30 -``` - -To modify how long should the `subscriber` take to process each message adjust `PROCESS_DURATION` in [deployment/subscriber.yaml](deployment/subscriber.yaml) and re-apply it to the cluster: - -```yaml -- name: PROCESS_DURATION - value: "300ms" -``` - -Finally, to adjust the number of messages published by the producer change the `producer` in [deployment/producer.yaml](./deployment/producer.yaml) and re-apply it to the cluster: - - -```yaml -- name: NUMBER_OF_PUBLISHERS - value: "1" -- name: PUBLISHERS_FREQ - value: "100ms" -``` - -The `NUMBER_OF_PUBLISHERS` setting is number of channels that are used to publish events (default: 1). And the `PUBLISHERS_FREQ` is the frequency with which each channel publishes events (default: 1s). - -> There is a limit to the amount of messages a single container can produce. If you need to scale beyond that number, increase the number of `autoscaling-producer` replicas - -```shell -kubectl scale -n kafka deployment/autoscaling-producer --replicas=10 -``` - -### Updating Components - -If you have changed already deployed Dapr component, make sure to reload the `subscriber` and `producer` deployments: - -```shell -kubectl rollout restart deployment/autoscaling-subscriber -kubectl rollout status deployment/autoscaling-subscriber -kubectl rollout restart deployment/autoscaling-producer -kubectl rollout status deployment/autoscaling-producer -``` - -### Kafka Helpers - -Get `metric` topic offsets for `autoscaling-subscriber` consumer group: - -```shell -kubectl -n kafka exec -it kafka-client -- kafka-consumer-groups \ - --bootstrap-server kafka-cp-kafka:9092 \ - --describe \ - --group autoscaling-subscriber -``` - -Purge the `metric` topic: - -```shell -kubectl -n kafka exec -it kafka-client -- kafka-topics \ - --zookeeper kafka-cp-zookeeper:2181 \ - --alter \ - --topic metric \ - --config retention.ms=1000 -sleep 15 -kubectl -n kafka exec -it kafka-client -- kafka-topics \ - --zookeeper kafka-cp-zookeeper:2181 \ - --alter \ - --topic metric \ - --delete-config retention.ms -``` - -Delete `metric` topic - -```shell -kubectl -n kafka exec -it kafka-client -- kafka-topics \ - --zookeeper kafka-cp-zookeeper:2181 \ - --delete \ - --topic metric -``` - -## Cleanup - -```shell -kubectl delete -f deployment/producer.yaml -kubectl delete -f deployment/kafka-pubsub.yaml -kubectl delete -f deployment/subscriber.yaml -kubectl delete -f deployment/subscriber-scaler.yaml -kubectl delete -f deployment/keda-2.0.0-beta.yaml -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Autoscaling Dapr service based on queue depth + +Dapr, with its modular building-block approach, along with the 10+ different Pub/Sub components, makes it easy to write message processing microservices. Since Dapr runs in VM, on bare-metal, in the Cloud, or even on the Edge... the autoscaling Dapr services is left to the hosting later. + +In case of Kubernetes, Dapr integrates with [Keda](https://github.com/kedacore/keda), an event driven autoscaler for Kubernetes. In this demo I'll walk through the configuration of Dapr microservice to scale along with the back pressure on [Kafka](https://kafka.apache.org) queue that service processes. + +![](image/diagram.png) + +## Setup + +The autoscaling demo requires [Dapr](https://dapr.io). If you don't already have a Kubernetes cluster with Dapr installed you can use the included [setup](../setup) to configure all the dependencies. + +### Keda + +Start by install [Keda](https://github.com/kedacore/keda) into the cluster and wait for it become ready: + +```shell +kubectl apply -f deployment/keda-2.0.0-beta.yaml +kubectl rollout status deployment.apps/keda-operator -n keda +``` + +### Kafka (optional) + +Next, if you don't have access to Kafka you can use these instructions to install Kafka into the cluster: + +```shell +helm repo add confluentinc https://confluentinc.github.io/cp-helm-charts/ +helm repo update +kubectl create ns kafka +helm install kafka confluentinc/cp-helm-charts -n kafka \ + --set cp-schema-registry.enabled=false \ + --set cp-kafka-rest.enabled=false \ + --set cp-kafka-connect.enabled=false \ + --set dataLogDirStorageClass=default \ + --set dataDirStorageClass=default \ + --set storageClass=default +kubectl rollout status deployment.apps/kafka-cp-control-center -n kafka +kubectl rollout status deployment.apps/kafka-cp-ksql-server -n kafka +kubectl rollout status statefulset.apps/kafka-cp-kafka -n kafka +kubectl rollout status statefulset.apps/kafka-cp-zookeeper -n kafka +``` + +When done, also deploy Kafka client and wait until it's ready: + +```shell +kubectl apply -n kafka -f deployment/kafka-client.yaml +kubectl wait -n kafka --for=condition=ready pod kafka-client --timeout=120s +``` + +Next, create the `metric` topic which we will use in this demo: + +> The number of `partitions` is connected to the maximum number of replicas Keda will create. + +```shell +kubectl -n kafka exec -it kafka-client -- kafka-topics \ + --zookeeper kafka-cp-zookeeper-headless:2181 \ + --topic metric \ + --create \ + --partitions 10 \ + --replication-factor 3 \ + --if-not-exists +``` + +## Deployment + +To configure the autoscaling demo we will deploy two deployments: `subscriber` which will be used to process messages of the `metric` queue in Kafka, and the `producer`, which will be publishing messages. To make the `producer` compatible with any one of the Pub/Sub components supported by Dapr we will publish the events onto the Kafka queue using Dapr APIs. + +### Subscriber + +The `subscriber` service doesn't really do anything with the messages. To resemble real-life processing which may take some time to process messages, the `subscriber` allows for explicit processing time setting. The default value is `300ms`. We will go over how to modify that later. + +To deploy the `subscriber` service, apply the [Kafka Dapr component](deployment/kafka-pubsub.yaml), the [message subscriber service](deployment/subscriber.yaml), and the [subscriber service Keda scaler](subscriber-scaler.yaml): + +```shell +kubectl apply -f deployment/kafka-pubsub.yaml +kubectl apply -f deployment/subscriber.yaml +kubectl apply -f deployment/subscriber-scaler.yaml +``` + +When done, start watching for the number of replicas of the deployed `subscriber` service: + +```shell +watch kubectl get pods -l app=autoscaling-subscriber +``` + +> Note, by default the subscriber service Keda scaler is set to scale to 0, so you will not see any pods yet. We will publish data on the `metric` topic with the `producer`. + +### Producer + +In a second terminal session, deploy the [producer service](deployment/producer.yaml) and wait for it to be ready: + +```shell +kubectl apply -f deployment/producer.yaml +kubectl rollout status deployment/autoscaling-producer +``` + +## Demo + +Back in the initial terminal now, some 20-30 seconds after the `producer` starts, you should see the number of `subscriber` pods being adjusted by Keda based on the number of the `metric` topic: + +```shell +NAME READY STATUS RESTARTS AGE +autoscaling-subscriber-696ffb5c7b-64zqq 2/2 Running 0 31s +autoscaling-subscriber-696ffb5c7b-67f74 2/2 Running 0 15s +autoscaling-subscriber-696ffb5c7b-gpc2d 2/2 Running 0 7m42s +``` + +By default the `subscriber-scaler` is set to scale-to-zero and has the polling frequency of `15s`. You can adjust these values in [deployment/subscriber-scaler.yaml](deployment/subscriber-scaler.yaml): + +```yaml +pollingInterval: 15 +minReplicaCount: 0 +maxReplicaCount: 10 +cooldownPeriod: 30 +``` + +To modify how long should the `subscriber` take to process each message adjust `PROCESS_DURATION` in [deployment/subscriber.yaml](deployment/subscriber.yaml) and re-apply it to the cluster: + +```yaml +- name: PROCESS_DURATION + value: "300ms" +``` + +Finally, to adjust the number of messages published by the producer change the `producer` in [deployment/producer.yaml](./deployment/producer.yaml) and re-apply it to the cluster: + + +```yaml +- name: NUMBER_OF_PUBLISHERS + value: "1" +- name: PUBLISHERS_FREQ + value: "100ms" +``` + +The `NUMBER_OF_PUBLISHERS` setting is number of channels that are used to publish events (default: 1). And the `PUBLISHERS_FREQ` is the frequency with which each channel publishes events (default: 1s). + +> There is a limit to the amount of messages a single container can produce. If you need to scale beyond that number, increase the number of `autoscaling-producer` replicas + +```shell +kubectl scale -n kafka deployment/autoscaling-producer --replicas=10 +``` + +### Updating Components + +If you have changed already deployed Dapr component, make sure to reload the `subscriber` and `producer` deployments: + +```shell +kubectl rollout restart deployment/autoscaling-subscriber +kubectl rollout status deployment/autoscaling-subscriber +kubectl rollout restart deployment/autoscaling-producer +kubectl rollout status deployment/autoscaling-producer +``` + +### Kafka Helpers + +Get `metric` topic offsets for `autoscaling-subscriber` consumer group: + +```shell +kubectl -n kafka exec -it kafka-client -- kafka-consumer-groups \ + --bootstrap-server kafka-cp-kafka:9092 \ + --describe \ + --group autoscaling-subscriber +``` + +Purge the `metric` topic: + +```shell +kubectl -n kafka exec -it kafka-client -- kafka-topics \ + --zookeeper kafka-cp-zookeeper:2181 \ + --alter \ + --topic metric \ + --config retention.ms=1000 +sleep 15 +kubectl -n kafka exec -it kafka-client -- kafka-topics \ + --zookeeper kafka-cp-zookeeper:2181 \ + --alter \ + --topic metric \ + --delete-config retention.ms +``` + +Delete `metric` topic + +```shell +kubectl -n kafka exec -it kafka-client -- kafka-topics \ + --zookeeper kafka-cp-zookeeper:2181 \ + --delete \ + --topic metric +``` + +## Cleanup + +```shell +kubectl delete -f deployment/producer.yaml +kubectl delete -f deployment/kafka-pubsub.yaml +kubectl delete -f deployment/subscriber.yaml +kubectl delete -f deployment/subscriber-scaler.yaml +kubectl delete -f deployment/keda-2.0.0-beta.yaml +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/autoscaling-on-queue/deployment/kafka-client.yaml b/autoscaling-on-queue/deployment/kafka-client.yaml index 9cfb391..b42f253 100644 --- a/autoscaling-on-queue/deployment/kafka-client.yaml +++ b/autoscaling-on-queue/deployment/kafka-client.yaml @@ -1,12 +1,12 @@ -apiVersion: v1 -kind: Pod -metadata: - name: kafka-client -spec: - containers: - - name: kafka-client - image: confluentinc/cp-enterprise-kafka:5.5.0 - command: - - sh - - -c +apiVersion: v1 +kind: Pod +metadata: + name: kafka-client +spec: + containers: + - name: kafka-client + image: confluentinc/cp-enterprise-kafka:5.5.0 + command: + - sh + - -c - "exec tail -f /dev/null" \ No newline at end of file diff --git a/autoscaling-on-queue/deployment/kafka-pubsub.yaml b/autoscaling-on-queue/deployment/kafka-pubsub.yaml index 088e373..00e2557 100644 --- a/autoscaling-on-queue/deployment/kafka-pubsub.yaml +++ b/autoscaling-on-queue/deployment/kafka-pubsub.yaml @@ -1,15 +1,15 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: autoscaling-pubsub -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: kafka-cp-kafka.kafka.svc.cluster.local:9092 - - name: authRequired - value: "false" - - name: allowedTopics - value: metric - - name: consumerID +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: autoscaling-pubsub +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: kafka-cp-kafka.kafka.svc.cluster.local:9092 + - name: authRequired + value: "false" + - name: allowedTopics + value: metric + - name: consumerID value: autoscaling-subscriber \ No newline at end of file diff --git a/autoscaling-on-queue/deployment/keda-2.0.0-beta.yaml b/autoscaling-on-queue/deployment/keda-2.0.0-beta.yaml index 2828e43..c4633ec 100644 --- a/autoscaling-on-queue/deployment/keda-2.0.0-beta.yaml +++ b/autoscaling-on-queue/deployment/keda-2.0.0-beta.yaml @@ -1,7144 +1,7144 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app.kubernetes.io/name: keda - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.3.0 - creationTimestamp: null - labels: - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: scaledjobs.keda.sh -spec: - additionalPrinterColumns: - - JSONPath: .spec.triggers[*].type - name: Triggers - type: string - - JSONPath: .spec.triggers[*].authenticationRef.name - name: Authentication - type: string - - JSONPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - JSONPath: .status.conditions[?(@.type=="Active")].status - name: Active - type: string - - JSONPath: .metadata.creationTimestamp - name: Age - type: date - group: keda.sh - names: - kind: ScaledJob - listKind: ScaledJobList - plural: scaledjobs - shortNames: - - sj - singular: scaledjob - scope: Namespaced - subresources: - status: {} - validation: - openAPIV3Schema: - description: ScaledJob is the Schema for the scaledjobs API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ScaledJobSpec defines the desired state of ScaledJob - properties: - envSourceContainerName: - type: string - failedJobsHistoryLimit: - format: int32 - type: integer - jobTargetRef: - description: JobSpec describes how the job execution will look like. - properties: - activeDeadlineSeconds: - description: Specifies the duration in seconds relative to the startTime - that the job may be active before the system tries to terminate - it; value must be positive integer - format: int64 - type: integer - backoffLimit: - description: Specifies the number of retries before marking this - job failed. Defaults to 6 - format: int32 - type: integer - completions: - description: 'Specifies the desired number of successfully finished - pods the job should be run with. Setting to nil means that the - success of any pod signals the success of all pods, and allows - parallelism to have any positive value. Setting to 1 means that - parallelism is limited to 1 and the success of that pod signals - the success of the job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/' - format: int32 - type: integer - manualSelector: - description: 'manualSelector controls generation of pod labels and - pod selectors. Leave `manualSelector` unset unless you are certain - what you are doing. When false or unset, the system pick labels - unique to this job and appends those labels to the pod template. When - true, the user is responsible for picking unique labels and specifying - the selector. Failure to pick a unique label may cause this and - other jobs to not function correctly. However, You may see `manualSelector=true` - in jobs that were created with the old `extensions/v1beta1` API. - More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector' - type: boolean - parallelism: - description: 'Specifies the maximum desired number of pods the job - should run at any given time. The actual number of pods running - in steady state will be less than this number when ((.spec.completions - - .status.successful) < .spec.parallelism), i.e. when the work - left to do is less than max parallelism. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/' - format: int32 - type: integer - selector: - description: 'A label query over pods that should match the pod - count. Normally, the system sets this field for you. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors' - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement is a selector that - contains values, a key, and an operator that relates the - key and values. - properties: - key: - description: key is the label key that the selector applies - to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of string values. If the - operator is In or NotIn, the values array must be non-empty. - If the operator is Exists or DoesNotExist, the values - array must be empty. This array is replaced during a - strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single - {key,value} in the matchLabels map is equivalent to an element - of matchExpressions, whose key field is "key", the operator - is "In", and the values array contains only "value". The requirements - are ANDed. - type: object - type: object - template: - description: 'Describes the pod that will be created when executing - a job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/' - properties: - metadata: - description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' - type: object - spec: - description: 'Specification of the desired behavior of the pod. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' - properties: - activeDeadlineSeconds: - description: Optional duration in seconds the pod may be - active on the node relative to StartTime before the system - will actively try to mark it failed and kill associated - containers. Value must be a positive integer. - format: int64 - type: integer - affinity: - description: If specified, the pod's scheduling constraints - properties: - nodeAffinity: - description: Describes node affinity scheduling rules - for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node matches the corresponding - matchExpressions; the node(s) with the highest - sum are the most preferred. - items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, in the - range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to an update), the system may or may - not try to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: A null or empty node selector - term matches no objects. The requirements - of them are ANDed. The TopologySelectorTerm - type implements a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - description: Describes pod affinity scheduling rules - (e.g. co-locate this pod in the same node, zone, etc. - as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added per-node - to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set - of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaces: - description: namespaces specifies which - namespaces the labelSelector applies - to (matches against); null or empty - list means "this pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies which namespaces - the labelSelector applies to (matches against); - null or empty list means "this pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling - rules (e.g. avoid putting this pod in the same node, - zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added per-node - to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set - of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - namespaces: - description: namespaces specifies which - namespaces the labelSelector applies - to (matches against); null or empty - list means "this pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the anti-affinity requirements specified by this - field cease to be met at some point during pod - execution (e.g. due to a pod label update), the - system may or may not try to eventually evict - the pod from its node. When there are multiple - elements, the lists of nodes corresponding to - each podAffinityTerm are intersected, i.e. all - terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies which namespaces - the labelSelector applies to (matches against); - null or empty list means "this pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - automountServiceAccountToken: - description: AutomountServiceAccountToken indicates whether - a service account token should be automatically mounted. - type: boolean - containers: - description: List of containers belonging to the pod. Containers - cannot currently be added or removed. There must be at - least one container in a Pod. Cannot be updated. - items: - description: A single application container that you want - to run within a pod. - properties: - args: - description: 'Arguments to the entrypoint. The docker - image''s CMD is used if this is not provided. Variable - references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the - reference in the input string will be unchanged. - The $(VAR_NAME) syntax can be escaped with a double - $$, ie: $$(VAR_NAME). Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed within - a shell. The docker image''s ENTRYPOINT is used - if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the reference - in the input string will be unchanged. The $(VAR_NAME) - syntax can be escaped with a double $$, ie: $$(VAR_NAME). - Escaped references will never be expanded, regardless - of whether the variable exists or not. Cannot be - updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables to set - in the container. Cannot be updated. - items: - description: EnvVar represents an environment variable - present in a Container. - properties: - name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) - are expanded using the previous defined environment - variables in the container and any service - environment variables. If a variable cannot - be resolved, the reference in the input string - will be unchanged. The $(VAR_NAME) syntax - can be escaped with a double $$, ie: $$(VAR_NAME). - Escaped references will never be expanded, - regardless of whether the variable exists - or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's - value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: - supports metadata.name, metadata.namespace, - metadata.labels, metadata.annotations, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, - defaults to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the - container: only resources limits and requests - (limits.cpu, limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults - to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to - select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in - the pod's namespace - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment - variables in the container. The keys defined within - a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container - is starting. When a key exists in multiple sources, - the value associated with the last source will take - precedence. Values defined by an Env with a duplicate - key will take precedence. Cannot be updated. - items: - description: EnvFromSource represents the source - of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - must be defined - type: boolean - type: object - prefix: - description: An optional identifier to prepend - to each key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config - management to default or override container images - in workload controllers like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, Never, - IfNotPresent. Defaults to Always if :latest tag - is specified, or IfNotPresent otherwise. Cannot - be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system should - take in response to container lifecycle events. - Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the handler - fails, the container is terminated and restarted - according to its restart policy. Other management - of the container blocks until the hook completes. - More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the - action to take. - properties: - command: - description: Command is the command line - to execute inside the container, the - working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it is - not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to explicitly - call out to that shell. Exit status - of 0 is treated as live/healthy and - non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, - defaults to the pod IP. You probably - want to set "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in - the request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a - custom header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP - server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not yet - supported TODO: implement a realistic TCP - lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately before - a container is terminated due to an API request - or management event such as liveness/startup - probe failure, preemption, resource contention, - etc. The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s termination - grace period countdown begins before the PreStop - hooked is executed. Regardless of the outcome - of the handler, the container will eventually - terminate within the Pod''s termination grace - period. Other management of the container blocks - until the hook completes or until the termination - grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the - action to take. - properties: - command: - description: Command is the command line - to execute inside the container, the - working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it is - not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to explicitly - call out to that shell. Exit status - of 0 is treated as live/healthy and - non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, - defaults to the pod IP. You probably - want to set "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in - the request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a - custom header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP - server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not yet - supported TODO: implement a realistic TCP - lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container liveness. - Container will be restarted if the probe fails. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified as a - DNS_LABEL. Each container in a pod must have a unique - name (DNS_LABEL). Cannot be updated. - type: string - ports: - description: List of ports to expose from the container. - Exposing a port here gives the system additional - information about the network connections a container - uses, but is primarily informational. Not specifying - a port here DOES NOT prevent that port from being - exposed. Any port which is listening on the default - "0.0.0.0" address inside a container will be accessible - from the network. Cannot be updated. - items: - description: ContainerPort represents a network - port in a single container. - properties: - containerPort: - description: Number of port to expose on the - pod's IP address. This must be a valid port - number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external - port to. - type: string - hostPort: - description: Number of port to expose on the - host. If specified, this must be a valid port - number, 0 < x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most containers - do not need this. - format: int32 - type: integer - name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port - in a pod must have a unique name. Name for - the port that can be referred to by services. - type: string - protocol: - description: Protocol for port. Must be UDP, - TCP, or SCTP. Defaults to "TCP". - type: string - required: - - containerPort - - protocol - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container service - readiness. Container will be removed from service - endpoints if the probe fails. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - type: object - type: object - securityContext: - description: 'Security options the pod should run - with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ - More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls - whether a process can gain more privileges than - its parent process. This bool directly controls - if the no_new_privs flag will be set on the - container process. AllowPrivilegeEscalation - is true always when the container is: 1) run - as Privileged 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop when - running containers. Defaults to the default - set of capabilities granted by the container - runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX - capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX - capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. - Processes in privileged containers are essentially - equivalent to root on the host. Defaults to - false. - type: boolean - procMount: - description: procMount denotes the type of proc - mount to use for the containers. The default - is DefaultProcMount which uses the container - runtime defaults for readonly paths and masked - paths. This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of - the container process. Uses runtime default - if unset. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must - run as a non-root user. If true, the Kubelet - will validate the image at runtime to ensure - that it does not run as UID 0 (root) and fail - to start the container if it does. If unset - or false, no such validation will be performed. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of - the container process. Defaults to user specified - in image metadata if unspecified. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied - to the container. If unspecified, the container - runtime will allocate a random SELinux context - for each container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - properties: - level: - description: Level is SELinux level label - that applies to the container. - type: string - role: - description: Role is a SELinux role label - that applies to the container. - type: string - type: - description: Type is a SELinux type label - that applies to the container. - type: string - user: - description: User is a SELinux user label - that applies to the container. - type: string - type: object - windowsOptions: - description: The Windows specific settings applied - to all containers. If unspecified, the options - from the PodSecurityContext will be used. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the - GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential - spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the - name of the GMSA credential spec to use. - type: string - runAsUserName: - description: The UserName in Windows to run - the entrypoint of the container process. - Defaults to the user specified in image - metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that the Pod - has successfully initialized. If specified, no other - probes are executed until this completes successfully. - If this probe fails, the Pod will be restarted, - just as if the livenessProbe failed. This can be - used to provide different probe parameters at the - beginning of a Pod''s lifecycle, when it might take - a long time to load data or warm a cache, than during - steady-state operation. This cannot be updated. - This is a beta feature enabled by the StartupProbe - feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should allocate - a buffer for stdin in the container runtime. If - this is not set, reads from stdin in the container - will always result in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime should - close the stdin channel after it has been opened - by a single attach. When stdin is true the stdin - stream will remain open across multiple attach sessions. - If stdinOnce is set to true, stdin is opened on - container start, is empty until the first client - attaches to stdin, and then remains open and accepts - data until the client disconnects, at which time - stdin is closed and remains closed until the container - is restarted. If this flag is false, a container - processes that reads from stdin will never receive - an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the file to - which the container''s termination message will - be written is mounted into the container''s filesystem. - Message written is intended to be brief final status, - such as an assertion failure message. Will be truncated - by the node if greater than 4096 bytes. The total - message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot - be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination message - should be populated. File will use the contents - of terminationMessagePath to populate the container - status message on both success and failure. FallbackToLogsOnError - will use the last chunk of container log output - if the termination message file is empty and the - container exited with an error. The log output is - limited to 2048 bytes or 80 lines, whichever is - smaller. Defaults to File. Cannot be updated. - type: string - tty: - description: Whether this container should allocate - a TTY for itself, also requires 'stdin' to be true. - Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of block devices - to be used by the container. - items: - description: volumeDevice describes a mapping of - a raw block device within a container. - properties: - devicePath: - description: devicePath is the path inside of - the container that the device will be mapped - to. - type: string - name: - description: name must match the name of a persistentVolumeClaim - in the pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the container's - filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting of - a Volume within a container. - properties: - mountPath: - description: Path within the container at which - the volume should be mounted. Must not contain - ':'. - type: string - mountPropagation: - description: mountPropagation determines how - mounts are propagated from the host to container - and the other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write - otherwise (false or unspecified). Defaults - to false. - type: boolean - subPath: - description: Path within the volume from which - the container's volume should be mounted. - Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume - from which the container's volume should be - mounted. Behaves similarly to SubPath but - environment variable references $(VAR_NAME) - are expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr - and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. If not - specified, the container runtime's default will - be used, which might be configured in the container - image. Cannot be updated. - type: string - required: - - name - type: object - type: array - dnsConfig: - description: Specifies the DNS parameters of a pod. Parameters - specified here will be merged to the generated DNS configuration - based on DNSPolicy. - properties: - nameservers: - description: A list of DNS name server IP addresses. - This will be appended to the base nameservers generated - from DNSPolicy. Duplicated nameservers will be removed. - items: - type: string - type: array - options: - description: A list of DNS resolver options. This will - be merged with the base options generated from DNSPolicy. - Duplicated entries will be removed. Resolution options - given in Options will override those that appear in - the base DNSPolicy. - items: - description: PodDNSConfigOption defines DNS resolver - options of a pod. - properties: - name: - description: Required. - type: string - value: - type: string - type: object - type: array - searches: - description: A list of DNS search domains for host-name - lookup. This will be appended to the base search paths - generated from DNSPolicy. Duplicated search paths - will be removed. - items: - type: string - type: array - type: object - dnsPolicy: - description: Set DNS policy for the pod. Defaults to "ClusterFirst". - Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', - 'Default' or 'None'. DNS parameters given in DNSConfig - will be merged with the policy selected with DNSPolicy. - To have DNS options set along with hostNetwork, you have - to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. - type: string - enableServiceLinks: - description: 'EnableServiceLinks indicates whether information - about services should be injected into pod''s environment - variables, matching the syntax of Docker links. Optional: - Defaults to true.' - type: boolean - ephemeralContainers: - description: List of ephemeral containers run in this pod. - Ephemeral containers may be run in an existing pod to - perform user-initiated actions such as debugging. This - list cannot be specified when creating a pod, and it cannot - be modified by updating the pod spec. In order to add - an ephemeral container to an existing pod, use the pod's - ephemeralcontainers subresource. This field is alpha-level - and is only honored by servers that enable the EphemeralContainers - feature. - items: - description: An EphemeralContainer is a container that - may be added temporarily to an existing pod for user-initiated - activities such as debugging. Ephemeral containers have - no resource or scheduling guarantees, and they will - not be restarted when they exit or when a pod is removed - or restarted. If an ephemeral container causes a pod - to exceed its resource allocation, the pod may be evicted. - Ephemeral containers may not be added by directly updating - the pod spec. They must be added via the pod's ephemeralcontainers - subresource, and they will appear in the pod spec once - added. This is an alpha feature enabled by the EphemeralContainers - feature flag. - properties: - args: - description: 'Arguments to the entrypoint. The docker - image''s CMD is used if this is not provided. Variable - references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the - reference in the input string will be unchanged. - The $(VAR_NAME) syntax can be escaped with a double - $$, ie: $$(VAR_NAME). Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed within - a shell. The docker image''s ENTRYPOINT is used - if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the reference - in the input string will be unchanged. The $(VAR_NAME) - syntax can be escaped with a double $$, ie: $$(VAR_NAME). - Escaped references will never be expanded, regardless - of whether the variable exists or not. Cannot be - updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables to set - in the container. Cannot be updated. - items: - description: EnvVar represents an environment variable - present in a Container. - properties: - name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) - are expanded using the previous defined environment - variables in the container and any service - environment variables. If a variable cannot - be resolved, the reference in the input string - will be unchanged. The $(VAR_NAME) syntax - can be escaped with a double $$, ie: $$(VAR_NAME). - Escaped references will never be expanded, - regardless of whether the variable exists - or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's - value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: - supports metadata.name, metadata.namespace, - metadata.labels, metadata.annotations, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, - defaults to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the - container: only resources limits and requests - (limits.cpu, limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults - to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to - select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in - the pod's namespace - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment - variables in the container. The keys defined within - a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container - is starting. When a key exists in multiple sources, - the value associated with the last source will take - precedence. Values defined by an Env with a duplicate - key will take precedence. Cannot be updated. - items: - description: EnvFromSource represents the source - of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - must be defined - type: boolean - type: object - prefix: - description: An optional identifier to prepend - to each key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, Never, - IfNotPresent. Defaults to Always if :latest tag - is specified, or IfNotPresent otherwise. Cannot - be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Lifecycle is not allowed for ephemeral - containers. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the handler - fails, the container is terminated and restarted - according to its restart policy. Other management - of the container blocks until the hook completes. - More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the - action to take. - properties: - command: - description: Command is the command line - to execute inside the container, the - working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it is - not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to explicitly - call out to that shell. Exit status - of 0 is treated as live/healthy and - non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, - defaults to the pod IP. You probably - want to set "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in - the request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a - custom header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP - server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not yet - supported TODO: implement a realistic TCP - lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately before - a container is terminated due to an API request - or management event such as liveness/startup - probe failure, preemption, resource contention, - etc. The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s termination - grace period countdown begins before the PreStop - hooked is executed. Regardless of the outcome - of the handler, the container will eventually - terminate within the Pod''s termination grace - period. Other management of the container blocks - until the hook completes or until the termination - grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the - action to take. - properties: - command: - description: Command is the command line - to execute inside the container, the - working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it is - not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to explicitly - call out to that shell. Exit status - of 0 is treated as live/healthy and - non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, - defaults to the pod IP. You probably - want to set "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in - the request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a - custom header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP - server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not yet - supported TODO: implement a realistic TCP - lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: Probes are not allowed for ephemeral - containers. - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the ephemeral container specified - as a DNS_LABEL. This name must be unique among all - containers, init containers and ephemeral containers. - type: string - ports: - description: Ports are not allowed for ephemeral containers. - items: - description: ContainerPort represents a network - port in a single container. - properties: - containerPort: - description: Number of port to expose on the - pod's IP address. This must be a valid port - number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external - port to. - type: string - hostPort: - description: Number of port to expose on the - host. If specified, this must be a valid port - number, 0 < x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most containers - do not need this. - format: int32 - type: integer - name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port - in a pod must have a unique name. Name for - the port that can be referred to by services. - type: string - protocol: - description: Protocol for port. Must be UDP, - TCP, or SCTP. Defaults to "TCP". - type: string - required: - - containerPort - - protocol - type: object - type: array - readinessProbe: - description: Probes are not allowed for ephemeral - containers. - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: Resources are not allowed for ephemeral - containers. Ephemeral containers use spare resources - already allocated to the pod. - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - type: object - type: object - securityContext: - description: SecurityContext is not allowed for ephemeral - containers. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls - whether a process can gain more privileges than - its parent process. This bool directly controls - if the no_new_privs flag will be set on the - container process. AllowPrivilegeEscalation - is true always when the container is: 1) run - as Privileged 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop when - running containers. Defaults to the default - set of capabilities granted by the container - runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX - capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX - capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. - Processes in privileged containers are essentially - equivalent to root on the host. Defaults to - false. - type: boolean - procMount: - description: procMount denotes the type of proc - mount to use for the containers. The default - is DefaultProcMount which uses the container - runtime defaults for readonly paths and masked - paths. This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of - the container process. Uses runtime default - if unset. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must - run as a non-root user. If true, the Kubelet - will validate the image at runtime to ensure - that it does not run as UID 0 (root) and fail - to start the container if it does. If unset - or false, no such validation will be performed. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of - the container process. Defaults to user specified - in image metadata if unspecified. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied - to the container. If unspecified, the container - runtime will allocate a random SELinux context - for each container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - properties: - level: - description: Level is SELinux level label - that applies to the container. - type: string - role: - description: Role is a SELinux role label - that applies to the container. - type: string - type: - description: Type is a SELinux type label - that applies to the container. - type: string - user: - description: User is a SELinux user label - that applies to the container. - type: string - type: object - windowsOptions: - description: The Windows specific settings applied - to all containers. If unspecified, the options - from the PodSecurityContext will be used. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the - GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential - spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the - name of the GMSA credential spec to use. - type: string - runAsUserName: - description: The UserName in Windows to run - the entrypoint of the container process. - Defaults to the user specified in image - metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: string - type: object - type: object - startupProbe: - description: Probes are not allowed for ephemeral - containers. - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should allocate - a buffer for stdin in the container runtime. If - this is not set, reads from stdin in the container - will always result in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime should - close the stdin channel after it has been opened - by a single attach. When stdin is true the stdin - stream will remain open across multiple attach sessions. - If stdinOnce is set to true, stdin is opened on - container start, is empty until the first client - attaches to stdin, and then remains open and accepts - data until the client disconnects, at which time - stdin is closed and remains closed until the container - is restarted. If this flag is false, a container - processes that reads from stdin will never receive - an EOF. Default is false - type: boolean - targetContainerName: - description: If set, the name of the container from - PodSpec that this ephemeral container targets. The - ephemeral container will be run in the namespaces - (IPC, PID, etc) of this container. If not set then - the ephemeral container is run in whatever namespaces - are shared for the pod. Note that the container - runtime must support this feature. - type: string - terminationMessagePath: - description: 'Optional: Path at which the file to - which the container''s termination message will - be written is mounted into the container''s filesystem. - Message written is intended to be brief final status, - such as an assertion failure message. Will be truncated - by the node if greater than 4096 bytes. The total - message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot - be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination message - should be populated. File will use the contents - of terminationMessagePath to populate the container - status message on both success and failure. FallbackToLogsOnError - will use the last chunk of container log output - if the termination message file is empty and the - container exited with an error. The log output is - limited to 2048 bytes or 80 lines, whichever is - smaller. Defaults to File. Cannot be updated. - type: string - tty: - description: Whether this container should allocate - a TTY for itself, also requires 'stdin' to be true. - Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of block devices - to be used by the container. - items: - description: volumeDevice describes a mapping of - a raw block device within a container. - properties: - devicePath: - description: devicePath is the path inside of - the container that the device will be mapped - to. - type: string - name: - description: name must match the name of a persistentVolumeClaim - in the pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the container's - filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting of - a Volume within a container. - properties: - mountPath: - description: Path within the container at which - the volume should be mounted. Must not contain - ':'. - type: string - mountPropagation: - description: mountPropagation determines how - mounts are propagated from the host to container - and the other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write - otherwise (false or unspecified). Defaults - to false. - type: boolean - subPath: - description: Path within the volume from which - the container's volume should be mounted. - Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume - from which the container's volume should be - mounted. Behaves similarly to SubPath but - environment variable references $(VAR_NAME) - are expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr - and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. If not - specified, the container runtime's default will - be used, which might be configured in the container - image. Cannot be updated. - type: string - required: - - name - type: object - type: array - hostAliases: - description: HostAliases is an optional list of hosts and - IPs that will be injected into the pod's hosts file if - specified. This is only valid for non-hostNetwork pods. - items: - description: HostAlias holds the mapping between IP and - hostnames that will be injected as an entry in the pod's - hosts file. - properties: - hostnames: - description: Hostnames for the above IP address. - items: - type: string - type: array - ip: - description: IP address of the host file entry. - type: string - type: object - type: array - hostIPC: - description: 'Use the host''s ipc namespace. Optional: Default - to false.' - type: boolean - hostNetwork: - description: Host networking requested for this pod. Use - the host's network namespace. If this option is set, the - ports that will be used must be specified. Default to - false. - type: boolean - hostPID: - description: 'Use the host''s pid namespace. Optional: Default - to false.' - type: boolean - hostname: - description: Specifies the hostname of the Pod If not specified, - the pod's hostname will be set to a system-defined value. - type: string - imagePullSecrets: - description: 'ImagePullSecrets is an optional list of references - to secrets in the same namespace to use for pulling any - of the images used by this PodSpec. If specified, these - secrets will be passed to individual puller implementations - for them to use. For example, in the case of docker, only - DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' - items: - description: LocalObjectReference contains enough information - to let you locate the referenced object inside the same - namespace. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - type: object - type: array - initContainers: - description: 'List of initialization containers belonging - to the pod. Init containers are executed in order prior - to containers being started. If any init container fails, - the pod is considered to have failed and is handled according - to its restartPolicy. The name for an init container or - normal container must be unique among all containers. - Init containers may not have Lifecycle actions, Readiness - probes, Liveness probes, or Startup probes. The resourceRequirements - of an init container are taken into account during scheduling - by finding the highest request/limit for each resource - type, and then using the max of of that value or the sum - of the normal containers. Limits are applied to init containers - in a similar fashion. Init containers cannot currently - be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' - items: - description: A single application container that you want - to run within a pod. - properties: - args: - description: 'Arguments to the entrypoint. The docker - image''s CMD is used if this is not provided. Variable - references $(VAR_NAME) are expanded using the container''s - environment. If a variable cannot be resolved, the - reference in the input string will be unchanged. - The $(VAR_NAME) syntax can be escaped with a double - $$, ie: $$(VAR_NAME). Escaped references will never - be expanded, regardless of whether the variable - exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed within - a shell. The docker image''s ENTRYPOINT is used - if this is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the reference - in the input string will be unchanged. The $(VAR_NAME) - syntax can be escaped with a double $$, ie: $$(VAR_NAME). - Escaped references will never be expanded, regardless - of whether the variable exists or not. Cannot be - updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables to set - in the container. Cannot be updated. - items: - description: EnvVar represents an environment variable - present in a Container. - properties: - name: - description: Name of the environment variable. - Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) - are expanded using the previous defined environment - variables in the container and any service - environment variables. If a variable cannot - be resolved, the reference in the input string - will be unchanged. The $(VAR_NAME) syntax - can be escaped with a double $$, ie: $$(VAR_NAME). - Escaped references will never be expanded, - regardless of whether the variable exists - or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's - value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: - supports metadata.name, metadata.namespace, - metadata.labels, metadata.annotations, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, - defaults to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the - container: only resources limits and requests - (limits.cpu, limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults - to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to - select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in - the pod's namespace - properties: - key: - description: The key of the secret to - select from. Must be a valid secret - key. - type: string - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment - variables in the container. The keys defined within - a source must be a C_IDENTIFIER. All invalid keys - will be reported as an event when the container - is starting. When a key exists in multiple sources, - the value associated with the last source will take - precedence. Values defined by an Env with a duplicate - key will take precedence. Cannot be updated. - items: - description: EnvFromSource represents the source - of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - must be defined - type: boolean - type: object - prefix: - description: An optional identifier to prepend - to each key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More - info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher level config - management to default or override container images - in workload controllers like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of Always, Never, - IfNotPresent. Defaults to Always if :latest tag - is specified, or IfNotPresent otherwise. Cannot - be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management system should - take in response to container lifecycle events. - Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the handler - fails, the container is terminated and restarted - according to its restart policy. Other management - of the container blocks until the hook completes. - More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the - action to take. - properties: - command: - description: Command is the command line - to execute inside the container, the - working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it is - not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to explicitly - call out to that shell. Exit status - of 0 is treated as live/healthy and - non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, - defaults to the pod IP. You probably - want to set "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in - the request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a - custom header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP - server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not yet - supported TODO: implement a realistic TCP - lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately before - a container is terminated due to an API request - or management event such as liveness/startup - probe failure, preemption, resource contention, - etc. The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s termination - grace period countdown begins before the PreStop - hooked is executed. Regardless of the outcome - of the handler, the container will eventually - terminate within the Pod''s termination grace - period. Other management of the container blocks - until the hook completes or until the termination - grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the - action to take. - properties: - command: - description: Command is the command line - to execute inside the container, the - working directory for the command is - root ('/') in the container's filesystem. - The command is simply exec'd, it is - not run inside a shell, so traditional - shell instructions ('|', etc) won't - work. To use a shell, you need to explicitly - call out to that shell. Exit status - of 0 is treated as live/healthy and - non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, - defaults to the pod IP. You probably - want to set "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in - the request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a - custom header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP - server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies an action - involving a TCP port. TCP hooks not yet - supported TODO: implement a realistic TCP - lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port - to access on the container. Number must - be in the range 1 to 65535. Name must - be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container liveness. - Container will be restarted if the probe fails. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified as a - DNS_LABEL. Each container in a pod must have a unique - name (DNS_LABEL). Cannot be updated. - type: string - ports: - description: List of ports to expose from the container. - Exposing a port here gives the system additional - information about the network connections a container - uses, but is primarily informational. Not specifying - a port here DOES NOT prevent that port from being - exposed. Any port which is listening on the default - "0.0.0.0" address inside a container will be accessible - from the network. Cannot be updated. - items: - description: ContainerPort represents a network - port in a single container. - properties: - containerPort: - description: Number of port to expose on the - pod's IP address. This must be a valid port - number, 0 < x < 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind the external - port to. - type: string - hostPort: - description: Number of port to expose on the - host. If specified, this must be a valid port - number, 0 < x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most containers - do not need this. - format: int32 - type: integer - name: - description: If specified, this must be an IANA_SVC_NAME - and unique within the pod. Each named port - in a pod must have a unique name. Name for - the port that can be referred to by services. - type: string - protocol: - description: Protocol for port. Must be UDP, - TCP, or SCTP. Defaults to "TCP". - type: string - required: - - containerPort - - protocol - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container service - readiness. Container will be removed from service - endpoints if the probe fails. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: 'Compute Resources required by this container. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is - omitted for a container, it defaults to Limits - if that is explicitly specified, otherwise to - an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' - type: object - type: object - securityContext: - description: 'Security options the pod should run - with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ - More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls - whether a process can gain more privileges than - its parent process. This bool directly controls - if the no_new_privs flag will be set on the - container process. AllowPrivilegeEscalation - is true always when the container is: 1) run - as Privileged 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop when - running containers. Defaults to the default - set of capabilities granted by the container - runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX - capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX - capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. - Processes in privileged containers are essentially - equivalent to root on the host. Defaults to - false. - type: boolean - procMount: - description: procMount denotes the type of proc - mount to use for the containers. The default - is DefaultProcMount which uses the container - runtime defaults for readonly paths and masked - paths. This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of - the container process. Uses runtime default - if unset. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must - run as a non-root user. If true, the Kubelet - will validate the image at runtime to ensure - that it does not run as UID 0 (root) and fail - to start the container if it does. If unset - or false, no such validation will be performed. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of - the container process. Defaults to user specified - in image metadata if unspecified. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied - to the container. If unspecified, the container - runtime will allocate a random SELinux context - for each container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - properties: - level: - description: Level is SELinux level label - that applies to the container. - type: string - role: - description: Role is a SELinux role label - that applies to the container. - type: string - type: - description: Type is a SELinux type label - that applies to the container. - type: string - user: - description: User is a SELinux user label - that applies to the container. - type: string - type: object - windowsOptions: - description: The Windows specific settings applied - to all containers. If unspecified, the options - from the PodSecurityContext will be used. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes - precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the - GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential - spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the - name of the GMSA credential spec to use. - type: string - runAsUserName: - description: The UserName in Windows to run - the entrypoint of the container process. - Defaults to the user specified in image - metadata if unspecified. May also be set - in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that the Pod - has successfully initialized. If specified, no other - probes are executed until this completes successfully. - If this probe fails, the Pod will be restarted, - just as if the livenessProbe failed. This can be - used to provide different probe parameters at the - beginning of a Pod''s lifecycle, when it might take - a long time to load data or warm a cache, than during - steady-state operation. This cannot be updated. - This is a beta feature enabled by the StartupProbe - feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the following - should be specified. Exec specifies the action - to take. - properties: - command: - description: Command is the command line to - execute inside the container, the working - directory for the command is root ('/') - in the container's filesystem. The command - is simply exec'd, it is not run inside a - shell, so traditional shell instructions - ('|', etc) won't work. To use a shell, you - need to explicitly call out to that shell. - Exit status of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures for - the probe to be considered failed after having - succeeded. Defaults to 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http request - to perform. - properties: - host: - description: Host name to connect to, defaults - to the pod IP. You probably want to set - "Host" in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set in the - request. HTTP allows repeated headers. - items: - description: HTTPHeader describes a custom - header to be used in HTTP probes - properties: - name: - description: The header field name - type: string - value: - description: The header field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after the container - has started before liveness probes are initiated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) to perform - the probe. Default to 10 seconds. Minimum value - is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes for - the probe to be considered successful after - having failed. Defaults to 1. Must be 1 for - liveness and startup. Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an action involving - a TCP port. TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name to connect - to, defaults to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the port to - access on the container. Number must be - in the range 1 to 65535. Name must be an - IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - timeoutSeconds: - description: 'Number of seconds after which the - probe times out. Defaults to 1 second. Minimum - value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should allocate - a buffer for stdin in the container runtime. If - this is not set, reads from stdin in the container - will always result in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime should - close the stdin channel after it has been opened - by a single attach. When stdin is true the stdin - stream will remain open across multiple attach sessions. - If stdinOnce is set to true, stdin is opened on - container start, is empty until the first client - attaches to stdin, and then remains open and accepts - data until the client disconnects, at which time - stdin is closed and remains closed until the container - is restarted. If this flag is false, a container - processes that reads from stdin will never receive - an EOF. Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the file to - which the container''s termination message will - be written is mounted into the container''s filesystem. - Message written is intended to be brief final status, - such as an assertion failure message. Will be truncated - by the node if greater than 4096 bytes. The total - message length across all containers will be limited - to 12kb. Defaults to /dev/termination-log. Cannot - be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination message - should be populated. File will use the contents - of terminationMessagePath to populate the container - status message on both success and failure. FallbackToLogsOnError - will use the last chunk of container log output - if the termination message file is empty and the - container exited with an error. The log output is - limited to 2048 bytes or 80 lines, whichever is - smaller. Defaults to File. Cannot be updated. - type: string - tty: - description: Whether this container should allocate - a TTY for itself, also requires 'stdin' to be true. - Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of block devices - to be used by the container. - items: - description: volumeDevice describes a mapping of - a raw block device within a container. - properties: - devicePath: - description: devicePath is the path inside of - the container that the device will be mapped - to. - type: string - name: - description: name must match the name of a persistentVolumeClaim - in the pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the container's - filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting of - a Volume within a container. - properties: - mountPath: - description: Path within the container at which - the volume should be mounted. Must not contain - ':'. - type: string - mountPropagation: - description: mountPropagation determines how - mounts are propagated from the host to container - and the other way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write - otherwise (false or unspecified). Defaults - to false. - type: boolean - subPath: - description: Path within the volume from which - the container's volume should be mounted. - Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume - from which the container's volume should be - mounted. Behaves similarly to SubPath but - environment variable references $(VAR_NAME) - are expanded using the container's environment. - Defaults to "" (volume's root). SubPathExpr - and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. If not - specified, the container runtime's default will - be used, which might be configured in the container - image. Cannot be updated. - type: string - required: - - name - type: object - type: array - nodeName: - description: NodeName is a request to schedule this pod - onto a specific node. If it is non-empty, the scheduler - simply schedules this pod onto that node, assuming that - it fits resource requirements. - type: string - nodeSelector: - additionalProperties: - type: string - description: 'NodeSelector is a selector which must be true - for the pod to fit on a node. Selector which must match - a node''s labels for the pod to be scheduled on that node. - More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' - type: object - overhead: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Overhead represents the resource overhead - associated with running a pod for a given RuntimeClass. - This field will be autopopulated at admission time by - the RuntimeClass admission controller. If the RuntimeClass - admission controller is enabled, overhead must not be - set in Pod create requests. The RuntimeClass admission - controller will reject Pod create requests which have - the overhead already set. If RuntimeClass is configured - and selected in the PodSpec, Overhead will be set to the - value defined in the corresponding RuntimeClass, otherwise - it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md - This field is alpha-level as of Kubernetes v1.16, and - is only honored by servers that enable the PodOverhead - feature.' - type: object - preemptionPolicy: - description: PreemptionPolicy is the Policy for preempting - pods with lower priority. One of Never, PreemptLowerPriority. - Defaults to PreemptLowerPriority if unset. This field - is alpha-level and is only honored by servers that enable - the NonPreemptingPriority feature. - type: string - priority: - description: The priority value. Various system components - use this field to find the priority of the pod. When Priority - Admission Controller is enabled, it prevents users from - setting this field. The admission controller populates - this field from PriorityClassName. The higher the value, - the higher the priority. - format: int32 - type: integer - priorityClassName: - description: If specified, indicates the pod's priority. - "system-node-critical" and "system-cluster-critical" are - two special keywords which indicate the highest priorities - with the former being the highest priority. Any other - name must be defined by creating a PriorityClass object - with that name. If not specified, the pod priority will - be default or zero if there is no default. - type: string - readinessGates: - description: 'If specified, all readiness gates will be - evaluated for pod readiness. A pod is ready when all its - containers are ready AND all conditions specified in the - readiness gates have status equal to "True" More info: - https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md' - items: - description: PodReadinessGate contains the reference to - a pod condition - properties: - conditionType: - description: ConditionType refers to a condition in - the pod's condition list with matching type. - type: string - required: - - conditionType - type: object - type: array - restartPolicy: - description: 'Restart policy for all containers within the - pod. One of Always, OnFailure, Never. Default to Always. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' - type: string - runtimeClassName: - description: 'RuntimeClassName refers to a RuntimeClass - object in the node.k8s.io group, which should be used - to run this pod. If no RuntimeClass resource matches - the named class, the pod will not be run. If unset or - empty, the "legacy" RuntimeClass will be used, which is - an implicit class with an empty definition that uses the - default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md - This is a beta feature as of Kubernetes v1.14.' - type: string - schedulerName: - description: If specified, the pod will be dispatched by - specified scheduler. If not specified, the pod will be - dispatched by default scheduler. - type: string - securityContext: - description: 'SecurityContext holds pod-level security attributes - and common container settings. Optional: Defaults to empty. See - type description for default values of each field.' - properties: - fsGroup: - description: "A special supplemental group that applies - to all containers in a pod. Some volume types allow - the Kubelet to change the ownership of that volume - to be owned by the pod: \n 1. The owning GID will - be the FSGroup 2. The setgid bit is set (new files - created in the volume will be owned by FSGroup) 3. - The permission bits are OR'd with rw-rw---- \n If - unset, the Kubelet will not modify the ownership and - permissions of any volume." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of - changing ownership and permission of the volume before - being exposed inside Pod. This field will only apply - to volume types which support fsGroup based ownership(and - permissions). It will have no effect on ephemeral - volume types such as: secret, configmaps and emptydir. - Valid values are "OnRootMismatch" and "Always". If - not specified defaults to "Always".' - type: string - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be - set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as - a non-root user. If true, the Kubelet will validate - the image at runtime to ensure that it does not run - as UID 0 (root) and fail to start the container if - it does. If unset or false, no such validation will - be performed. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence - for that container. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to all - containers. If unspecified, the container runtime - will allocate a random SELinux context for each container. May - also be set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - supplementalGroups: - description: A list of groups applied to the first process - run in each container, in addition to the container's - primary GID. If unspecified, no groups will be added - to any container. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced sysctls - used for the pod. Pods with unsupported sysctls (by - the container runtime) might fail to launch. - items: - description: Sysctl defines a kernel parameter to - be set - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - description: The Windows specific settings applied to - all containers. If unspecified, the options within - a container's SecurityContext will be used. If set - in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA - admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec - named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name - of the GMSA credential spec to use. - type: string - runAsUserName: - description: The UserName in Windows to run the - entrypoint of the container process. Defaults - to the user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - serviceAccount: - description: 'DeprecatedServiceAccount is a depreciated - alias for ServiceAccountName. Deprecated: Use serviceAccountName - instead.' - type: string - serviceAccountName: - description: 'ServiceAccountName is the name of the ServiceAccount - to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' - type: string - shareProcessNamespace: - description: 'Share a single process namespace between all - of the containers in a pod. When this is set containers - will be able to view and signal processes from other containers - in the same pod, and the first process in each container - will not be assigned PID 1. HostPID and ShareProcessNamespace - cannot both be set. Optional: Default to false.' - type: boolean - subdomain: - description: If specified, the fully qualified Pod hostname - will be "...svc.". If not specified, the pod will not have a domainname - at all. - type: string - terminationGracePeriodSeconds: - description: Optional duration in seconds the pod needs - to terminate gracefully. May be decreased in delete request. - Value must be non-negative integer. The value zero indicates - delete immediately. If this value is nil, the default - grace period will be used instead. The grace period is - the duration in seconds after the processes running in - the pod are sent a termination signal and the time when - the processes are forcibly halted with a kill signal. - Set this value longer than the expected cleanup time for - your process. Defaults to 30 seconds. - format: int64 - type: integer - tolerations: - description: If specified, the pod's tolerations. - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple - using the matching operator . - properties: - effect: - description: Effect indicates the taint effect to - match. Empty means match all taint effects. When - specified, allowed values are NoSchedule, PreferNoSchedule - and NoExecute. - type: string - key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If - the key is empty, operator must be Exists; this - combination means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints - of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect - NoExecute, otherwise this field is ignored) tolerates - the taint. By default, it is not set, which means - tolerate the taint forever (do not evict). Zero - and negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value - should be empty, otherwise just a regular string. - type: string - type: object - type: array - topologySpreadConstraints: - description: TopologySpreadConstraints describes how a group - of pods ought to spread across topology domains. Scheduler - will schedule pods in a way which abides by the constraints. - This field is only honored by clusters that enable the - EvenPodsSpread feature. All topologySpreadConstraints - are ANDed. - items: - description: TopologySpreadConstraint specifies how to - spread matching pods among the given topology. - properties: - labelSelector: - description: LabelSelector is used to find matching - pods. Pods that match this label selector are counted - to determine the number of pods in their corresponding - topology domain. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement is - a selector that contains values, a key, and - an operator that relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. If - the operator is Exists or DoesNotExist, - the values array must be empty. This array - is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is "In", - and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - maxSkew: - description: 'MaxSkew describes the degree to which - pods may be unevenly distributed. It''s the maximum - permitted difference between the number of matching - pods in any two topology domains of a given topology - type. For example, in a 3-zone cluster, MaxSkew - is set to 1, and pods with the same labelSelector - spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | - - if MaxSkew is 1, incoming pod can only be scheduled - to zone3 to become 1/1/1; scheduling it onto zone1(zone2) - would make the ActualSkew(2-0) on zone1(zone2) violate - MaxSkew(1). - if MaxSkew is 2, incoming pod can - be scheduled onto any zone. It''s a required field. - Default value is 1 and 0 is not allowed.' - format: int32 - type: integer - topologyKey: - description: TopologyKey is the key of node labels. - Nodes that have a label with this key and identical - values are considered to be in the same topology. - We consider each as a "bucket", and - try to put balanced number of pods into each bucket. - It's a required field. - type: string - whenUnsatisfiable: - description: 'WhenUnsatisfiable indicates how to deal - with a pod if it doesn''t satisfy the spread constraint. - - DoNotSchedule (default) tells the scheduler not - to schedule it - ScheduleAnyway tells the scheduler - to still schedule it It''s considered as "Unsatisfiable" - if and only if placing incoming pod on any topology - violates "MaxSkew". For example, in a 3-zone cluster, - MaxSkew is set to 1, and pods with the same labelSelector - spread as 3/1/1: | zone1 | zone2 | zone3 | | P P - P | P | P | If WhenUnsatisfiable is set - to DoNotSchedule, incoming pod can only be scheduled - to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) - on zone2(zone3) satisfies MaxSkew(1). In other words, - the cluster can still be imbalanced, but scheduler - won''t make it *more* imbalanced. It''s a required - field.' - type: string - required: - - maxSkew - - topologyKey - - whenUnsatisfiable - type: object - type: array - x-kubernetes-list-map-keys: - - topologyKey - - whenUnsatisfiable - x-kubernetes-list-type: map - volumes: - description: 'List of volumes that can be mounted by containers - belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' - items: - description: Volume represents a named volume in a pod - that may be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: 'AWSElasticBlockStore represents an AWS - Disk resource that is attached to a kubelet''s host - machine and then exposed to the pod. More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - properties: - fsType: - description: 'Filesystem type of the volume that - you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - partition: - description: 'The partition in the volume that - you want to mount. If omitted, the default is - to mount by volume name. Examples: For volume - /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty).' - format: int32 - type: integer - readOnly: - description: 'Specify "true" to force and set - the ReadOnly property in VolumeMounts to "true". - If omitted, the default is "false". More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: boolean - volumeID: - description: 'Unique ID of the persistent disk - resource in AWS (Amazon EBS volume). More info: - https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: string - required: - - volumeID - type: object - azureDisk: - description: AzureDisk represents an Azure Data Disk - mount on the host and bind mount to the pod. - properties: - cachingMode: - description: 'Host Caching mode: None, Read Only, - Read Write.' - type: string - diskName: - description: The Name of the data disk in the - blob storage - type: string - diskURI: - description: The URI the data disk in the blob - storage - type: string - fsType: - description: Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - kind: - description: 'Expected values Shared: multiple - blob disks per storage account Dedicated: single - blob disk per storage account Managed: azure - managed data disk (only in managed availability - set). defaults to shared' - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: AzureFile represents an Azure File Service - mount on the host and bind mount to the pod. - properties: - readOnly: - description: Defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretName: - description: the name of secret that contains - Azure Storage Account Name and Key - type: string - shareName: - description: Share Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: CephFS represents a Ceph FS mount on - the host that shares a pod's lifetime - properties: - monitors: - description: 'Required: Monitors is a collection - of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - items: - type: string - type: array - path: - description: 'Optional: Used as the mounted root, - rather than the full Ceph tree, default is /' - type: string - readOnly: - description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: boolean - secretFile: - description: 'Optional: SecretFile is the path - to key ring for User, default is /etc/ceph/user.secret - More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - secretRef: - description: 'Optional: SecretRef is reference - to the authentication secret for User, default - is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'Optional: User is the rados user - name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - required: - - monitors - type: object - cinder: - description: 'Cinder represents a cinder volume attached - and mounted on kubelets host machine. More info: - https://examples.k8s.io/mysql-cinder-pd/README.md' - properties: - fsType: - description: 'Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - readOnly: - description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: boolean - secretRef: - description: 'Optional: points to a secret object - containing parameters used to connect to OpenStack.' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeID: - description: 'volume id used to identify the volume - in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - required: - - volumeID - type: object - configMap: - description: ConfigMap represents a configMap that - should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 - and 0777. Defaults to 0644. Directories within - the path are not affected by this setting. This - might be in conflict with other options that - affect the file mode, like fsGroup, and the - result can be other mode bits set.' - format: int32 - type: integer - items: - description: If unspecified, each key-value pair - in the Data field of the referenced ConfigMap - will be projected into the volume as a file - whose name is the key and content is the value. - If specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the ConfigMap, the volume - setup will error unless it is marked optional. - Paths must be relative and may not contain the - '..' path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits to use - on this file, must be a value between - 0 and 0777. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - path: - description: The relative path of the file - to map the key to. May not be an absolute - path. May not contain the path element - '..'. May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, - uid?' - type: string - optional: - description: Specify whether the ConfigMap or - its keys must be defined - type: boolean - type: object - csi: - description: CSI (Container Storage Interface) represents - storage that is handled by an external CSI driver - (Alpha feature). - properties: - driver: - description: Driver is the name of the CSI driver - that handles this volume. Consult with your - admin for the correct name as registered in - the cluster. - type: string - fsType: - description: Filesystem type to mount. Ex. "ext4", - "xfs", "ntfs". If not provided, the empty value - is passed to the associated CSI driver which - will determine the default filesystem to apply. - type: string - nodePublishSecretRef: - description: NodePublishSecretRef is a reference - to the secret object containing sensitive information - to pass to the CSI driver to complete the CSI - NodePublishVolume and NodeUnpublishVolume calls. - This field is optional, and may be empty if - no secret is required. If the secret object - contains more than one secret, all secret references - are passed. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - readOnly: - description: Specifies a read-only configuration - for the volume. Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: VolumeAttributes stores driver-specific - properties that are passed to the CSI driver. - Consult your driver's documentation for supported - values. - type: object - required: - - driver - type: object - downwardAPI: - description: DownwardAPI represents downward API about - the pod that should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 - and 0777. Defaults to 0644. Directories within - the path are not affected by this setting. This - might be in conflict with other options that - affect the file mode, like fsGroup, and the - result can be other mode bits set.' - format: int32 - type: integer - items: - description: Items is a list of downward API volume - file - items: - description: DownwardAPIVolumeFile represents - information to create the file containing - the pod field - properties: - fieldRef: - description: 'Required: Selects a field - of the pod: only annotations, labels, - name and namespace are supported.' - properties: - apiVersion: - description: Version of the schema the - FieldPath is written in terms of, - defaults to "v1". - type: string - fieldPath: - description: Path of the field to select - in the specified API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits to use - on this file, must be a value between - 0 and 0777. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the relative - path name of the file to be created. Must - not be absolute or contain the ''..'' - path. Must be utf-8 encoded. The first - item of the relative path must not start - with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of the - container: only resources limits and requests - (limits.cpu, limits.memory, requests.cpu - and requests.memory) are currently supported.' - properties: - containerName: - description: 'Container name: required - for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format - of the exposed resources, defaults - to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to - select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - description: 'EmptyDir represents a temporary directory - that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - properties: - medium: - description: 'What type of storage medium should - back this directory. The default is "" which - means to use the node''s default medium. Must - be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'Total amount of local storage required - for this EmptyDir volume. The size limit is - also applicable for memory medium. The maximum - usage on memory medium EmptyDir would be the - minimum value between the SizeLimit specified - here and the sum of memory limits of all containers - in a pod. The default is nil which means that - the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - fc: - description: FC represents a Fibre Channel resource - that is attached to a kubelet's host machine and - then exposed to the pod. - properties: - fsType: - description: 'Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. TODO: - how do we prevent errors in the filesystem from - compromising the machine' - type: string - lun: - description: 'Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts.' - type: boolean - targetWWNs: - description: 'Optional: FC target worldwide names - (WWNs)' - items: - type: string - type: array - wwids: - description: 'Optional: FC volume world wide identifiers - (wwids) Either wwids or combination of targetWWNs - and lun must be set, but not both simultaneously.' - items: - type: string - type: array - type: object - flexVolume: - description: FlexVolume represents a generic volume - resource that is provisioned/attached using an exec - based plugin. - properties: - driver: - description: Driver is the name of the driver - to use for this volume. - type: string - fsType: - description: Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". The default - filesystem depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'Optional: Extra command options - if any.' - type: object - readOnly: - description: 'Optional: Defaults to false (read/write). - ReadOnly here will force the ReadOnly setting - in VolumeMounts.' - type: boolean - secretRef: - description: 'Optional: SecretRef is reference - to the secret object containing sensitive information - to pass to the plugin scripts. This may be empty - if no secret object is specified. If the secret - object contains more than one secret, all secrets - are passed to the plugin scripts.' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - required: - - driver - type: object - flocker: - description: Flocker represents a Flocker volume attached - to a kubelet's host machine. This depends on the - Flocker control service being running - properties: - datasetName: - description: Name of the dataset stored as metadata - -> name on the dataset for Flocker should be - considered as deprecated - type: string - datasetUUID: - description: UUID of the dataset. This is unique - identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: 'GCEPersistentDisk represents a GCE Disk - resource that is attached to a kubelet''s host machine - and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - properties: - fsType: - description: 'Filesystem type of the volume that - you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - partition: - description: 'The partition in the volume that - you want to mount. If omitted, the default is - to mount by volume name. Examples: For volume - /dev/sda1, you specify the partition as "1". - Similarly, the volume partition for /dev/sda - is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 - type: integer - pdName: - description: 'Unique name of the PD resource in - GCE. Used to identify the disk in GCE. More - info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: string - readOnly: - description: 'ReadOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: boolean - required: - - pdName - type: object - gitRepo: - description: 'GitRepo represents a git repository - at a particular revision. DEPRECATED: GitRepo is - deprecated. To provision a container with a git - repo, mount an EmptyDir into an InitContainer that - clones the repo using git, then mount the EmptyDir - into the Pod''s container.' - properties: - directory: - description: Target directory name. Must not contain - or start with '..'. If '.' is supplied, the - volume directory will be the git repository. Otherwise, - if specified, the volume will contain the git - repository in the subdirectory with the given - name. - type: string - repository: - description: Repository URL - type: string - revision: - description: Commit hash for the specified revision. - type: string - required: - - repository - type: object - glusterfs: - description: 'Glusterfs represents a Glusterfs mount - on the host that shares a pod''s lifetime. More - info: https://examples.k8s.io/volumes/glusterfs/README.md' - properties: - endpoints: - description: 'EndpointsName is the endpoint name - that details Glusterfs topology. More info: - https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - path: - description: 'Path is the Glusterfs volume path. - More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - readOnly: - description: 'ReadOnly here will force the Glusterfs - volume to be mounted with read-only permissions. - Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: 'HostPath represents a pre-existing file - or directory on the host machine that is directly - exposed to the container. This is generally used - for system agents or other privileged things that - are allowed to see the host machine. Most containers - will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use - host directory mounts and who can/can not mount - host directories as read/write.' - properties: - path: - description: 'Path of the directory on the host. - If the path is a symlink, it will follow the - link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'Type for HostPath Volume Defaults - to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - iscsi: - description: 'ISCSI represents an ISCSI Disk resource - that is attached to a kubelet''s host machine and - then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' - properties: - chapAuthDiscovery: - description: whether support iSCSI Discovery CHAP - authentication - type: boolean - chapAuthSession: - description: whether support iSCSI Session CHAP - authentication - type: boolean - fsType: - description: 'Filesystem type of the volume that - you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - initiatorName: - description: Custom iSCSI Initiator Name. If initiatorName - is specified with iscsiInterface simultaneously, - new iSCSI interface : will be created for the connection. - type: string - iqn: - description: Target iSCSI Qualified Name. - type: string - iscsiInterface: - description: iSCSI Interface Name that uses an - iSCSI transport. Defaults to 'default' (tcp). - type: string - lun: - description: iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: iSCSI Target Portal List. The portal - is either an IP or ip_addr:port if the port - is other than default (typically TCP ports 860 - and 3260). - items: - type: string - type: array - readOnly: - description: ReadOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. - type: boolean - secretRef: - description: CHAP Secret for iSCSI target and - initiator authentication - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - targetPortal: - description: iSCSI Target Portal. The Portal is - either an IP or ip_addr:port if the port is - other than default (typically TCP ports 860 - and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: 'Volume''s name. Must be a DNS_LABEL - and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - nfs: - description: 'NFS represents an NFS mount on the host - that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - properties: - path: - description: 'Path that is exported by the NFS - server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - readOnly: - description: 'ReadOnly here will force the NFS - export to be mounted with read-only permissions. - Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: boolean - server: - description: 'Server is the hostname or IP address - of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: 'PersistentVolumeClaimVolumeSource represents - a reference to a PersistentVolumeClaim in the same - namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - claimName: - description: 'ClaimName is the name of a PersistentVolumeClaim - in the same namespace as the pod using this - volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - type: string - readOnly: - description: Will force the ReadOnly setting in - VolumeMounts. Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: PhotonPersistentDisk represents a PhotonController - persistent disk attached and mounted on kubelets - host machine - properties: - fsType: - description: Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - pdID: - description: ID that identifies Photon Controller - persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: PortworxVolume represents a portworx - volume attached and mounted on kubelets host machine - properties: - fsType: - description: FSType represents the filesystem - type to mount Must be a filesystem type supported - by the host operating system. Ex. "ext4", "xfs". - Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - volumeID: - description: VolumeID uniquely identifies a Portworx - volume - type: string - required: - - volumeID - type: object - projected: - description: Items for all in one resources secrets, - configmaps, and downward API - properties: - defaultMode: - description: Mode bits to use on created files - by default. Must be a value between 0 and 0777. - Directories within the path are not affected - by this setting. This might be in conflict with - other options that affect the file mode, like - fsGroup, and the result can be other mode bits - set. - format: int32 - type: integer - sources: - description: list of volume projections - items: - description: Projection that may be projected - along with other supported volume types - properties: - configMap: - description: information about the configMap - data to project - properties: - items: - description: If unspecified, each key-value - pair in the Data field of the referenced - ConfigMap will be projected into the - volume as a file whose name is the - key and content is the value. If specified, - the listed keys will be projected - into the specified paths, and unlisted - keys will not be present. If a key - is specified which is not present - in the ConfigMap, the volume setup - will error unless it is marked optional. - Paths must be relative and may not - contain the '..' path or start with - '..'. - items: - description: Maps a string key to - a path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits - to use on this file, must be - a value between 0 and 0777. - If not specified, the volume - defaultMode will be used. This - might be in conflict with other - options that affect the file - mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: The relative path - of the file to map the key to. - May not be an absolute path. - May not contain the path element - '..'. May not start with the - string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap - or its keys must be defined - type: boolean - type: object - downwardAPI: - description: information about the downwardAPI - data to project - properties: - items: - description: Items is a list of DownwardAPIVolume - file - items: - description: DownwardAPIVolumeFile - represents information to create - the file containing the pod field - properties: - fieldRef: - description: 'Required: Selects - a field of the pod: only annotations, - labels, name and namespace are - supported.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is - written in terms of, defaults - to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits - to use on this file, must be - a value between 0 and 0777. - If not specified, the volume - defaultMode will be used. This - might be in conflict with other - options that affect the file - mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: 'Required: Path is the - relative path name of the file - to be created. Must not be absolute - or contain the ''..'' path. - Must be utf-8 encoded. The first - item of the relative path must - not start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, requests.cpu - and requests.memory) are currently - supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the - output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - description: information about the secret - data to project - properties: - items: - description: If unspecified, each key-value - pair in the Data field of the referenced - Secret will be projected into the - volume as a file whose name is the - key and content is the value. If specified, - the listed keys will be projected - into the specified paths, and unlisted - keys will not be present. If a key - is specified which is not present - in the Secret, the volume setup will - error unless it is marked optional. - Paths must be relative and may not - contain the '..' path or start with - '..'. - items: - description: Maps a string key to - a path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits - to use on this file, must be - a value between 0 and 0777. - If not specified, the volume - defaultMode will be used. This - might be in conflict with other - options that affect the file - mode, like fsGroup, and the - result can be other mode bits - set.' - format: int32 - type: integer - path: - description: The relative path - of the file to map the key to. - May not be an absolute path. - May not contain the path element - '..'. May not start with the - string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - optional: - description: Specify whether the Secret - or its key must be defined - type: boolean - type: object - serviceAccountToken: - description: information about the serviceAccountToken - data to project - properties: - audience: - description: Audience is the intended - audience of the token. A recipient - of a token must identify itself with - an identifier specified in the audience - of the token, and otherwise should - reject the token. The audience defaults - to the identifier of the apiserver. - type: string - expirationSeconds: - description: ExpirationSeconds is the - requested duration of validity of - the service account token. As the - token approaches expiration, the kubelet - volume plugin will proactively rotate - the service account token. The kubelet - will start trying to rotate the token - if the token is older than 80 percent - of its time to live or if the token - is older than 24 hours.Defaults to - 1 hour and must be at least 10 minutes. - format: int64 - type: integer - path: - description: Path is the path relative - to the mount point of the file to - project the token into. - type: string - required: - - path - type: object - type: object - type: array - required: - - sources - type: object - quobyte: - description: Quobyte represents a Quobyte mount on - the host that shares a pod's lifetime - properties: - group: - description: Group to map volume access to Default - is no group - type: string - readOnly: - description: ReadOnly here will force the Quobyte - volume to be mounted with read-only permissions. - Defaults to false. - type: boolean - registry: - description: Registry represents a single or multiple - Quobyte Registry services specified as a string - as host:port pair (multiple entries are separated - with commas) which acts as the central registry - for volumes - type: string - tenant: - description: Tenant owning the given Quobyte volume - in the Backend Used with dynamically provisioned - Quobyte volumes, value is set by the plugin - type: string - user: - description: User to map volume access to Defaults - to serivceaccount user - type: string - volume: - description: Volume is a string that references - an already created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: 'RBD represents a Rados Block Device - mount on the host that shares a pod''s lifetime. - More info: https://examples.k8s.io/volumes/rbd/README.md' - properties: - fsType: - description: 'Filesystem type of the volume that - you want to mount. Tip: Ensure that the filesystem - type is supported by the host operating system. - Examples: "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. More info: - https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem - from compromising the machine' - type: string - image: - description: 'The rados image name. More info: - https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - keyring: - description: 'Keyring is the path to key ring - for RBDUser. Default is /etc/ceph/keyring. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - monitors: - description: 'A collection of Ceph monitors. More - info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - items: - type: string - type: array - pool: - description: 'The rados pool name. Default is - rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - readOnly: - description: 'ReadOnly here will force the ReadOnly - setting in VolumeMounts. Defaults to false. - More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: boolean - secretRef: - description: 'SecretRef is name of the authentication - secret for RBDUser. If provided overrides keyring. - Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - user: - description: 'The rados user name. Default is - admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - required: - - image - - monitors - type: object - scaleIO: - description: ScaleIO represents a ScaleIO persistent - volume attached and mounted on Kubernetes nodes. - properties: - fsType: - description: Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Default is - "xfs". - type: string - gateway: - description: The host address of the ScaleIO API - Gateway. - type: string - protectionDomain: - description: The name of the ScaleIO Protection - Domain for the configured storage. - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: SecretRef references to the secret - for ScaleIO user and other sensitive information. - If this is not provided, Login operation will - fail. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - sslEnabled: - description: Flag to enable/disable SSL communication - with Gateway, default false - type: boolean - storageMode: - description: Indicates whether the storage for - a volume should be ThickProvisioned or ThinProvisioned. - Default is ThinProvisioned. - type: string - storagePool: - description: The ScaleIO Storage Pool associated - with the protection domain. - type: string - system: - description: The name of the storage system as - configured in ScaleIO. - type: string - volumeName: - description: The name of a volume already created - in the ScaleIO system that is associated with - this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: 'Secret represents a secret that should - populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - properties: - defaultMode: - description: 'Optional: mode bits to use on created - files by default. Must be a value between 0 - and 0777. Defaults to 0644. Directories within - the path are not affected by this setting. This - might be in conflict with other options that - affect the file mode, like fsGroup, and the - result can be other mode bits set.' - format: int32 - type: integer - items: - description: If unspecified, each key-value pair - in the Data field of the referenced Secret will - be projected into the volume as a file whose - name is the key and content is the value. If - specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the Secret, the volume setup - will error unless it is marked optional. Paths - must be relative and may not contain the '..' - path or start with '..'. - items: - description: Maps a string key to a path within - a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits to use - on this file, must be a value between - 0 and 0777. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' - format: int32 - type: integer - path: - description: The relative path of the file - to map the key to. May not be an absolute - path. May not contain the path element - '..'. May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: Specify whether the Secret or its - keys must be defined - type: boolean - secretName: - description: 'Name of the secret in the pod''s - namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - type: string - type: object - storageos: - description: StorageOS represents a StorageOS volume - attached and mounted on Kubernetes nodes. - properties: - fsType: - description: Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly - here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: SecretRef specifies the secret to - use for obtaining the StorageOS API credentials. If - not specified, default values will be attempted. - properties: - name: - description: 'Name of the referent. More info: - https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, - kind, uid?' - type: string - type: object - volumeName: - description: VolumeName is the human-readable - name of the StorageOS volume. Volume names - are only unique within a namespace. - type: string - volumeNamespace: - description: VolumeNamespace specifies the scope - of the volume within StorageOS. If no namespace - is specified then the Pod's namespace will be - used. This allows the Kubernetes name scoping - to be mirrored within StorageOS for tighter - integration. Set VolumeName to any name to override - the default behaviour. Set to "default" if you - are not using namespaces within StorageOS. Namespaces - that do not pre-exist within StorageOS will - be created. - type: string - type: object - vsphereVolume: - description: VsphereVolume represents a vSphere volume - attached and mounted on kubelets host machine - properties: - fsType: - description: Filesystem type to mount. Must be - a filesystem type supported by the host operating - system. Ex. "ext4", "xfs", "ntfs". Implicitly - inferred to be "ext4" if unspecified. - type: string - storagePolicyID: - description: Storage Policy Based Management (SPBM) - profile ID associated with the StoragePolicyName. - type: string - storagePolicyName: - description: Storage Policy Based Management (SPBM) - profile name. - type: string - volumePath: - description: Path that identifies vSphere volume - vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - required: - - containers - type: object - type: object - ttlSecondsAfterFinished: - description: ttlSecondsAfterFinished limits the lifetime of a Job - that has finished execution (either Complete or Failed). If this - field is set, ttlSecondsAfterFinished after the Job finishes, - it is eligible to be automatically deleted. When the Job is being - deleted, its lifecycle guarantees (e.g. finalizers) will be honored. - If this field is unset, the Job won't be automatically deleted. - If this field is set to zero, the Job becomes eligible to be deleted - immediately after it finishes. This field is alpha-level and is - only honored by servers that enable the TTLAfterFinished feature. - format: int32 - type: integer - required: - - template - type: object - maxReplicaCount: - format: int32 - type: integer - pollingInterval: - format: int32 - type: integer - successfulJobsHistoryLimit: - format: int32 - type: integer - triggers: - items: - description: ScaleTriggers reference the scaler that will be used - properties: - authenticationRef: - description: ScaledObjectAuthRef points to the TriggerAuthentication - object that is used to authenticate the scaler with the environment - properties: - name: - type: string - required: - - name - type: object - metadata: - additionalProperties: - type: string - type: object - name: - type: string - type: - type: string - required: - - metadata - - type - type: object - type: array - required: - - jobTargetRef - - triggers - type: object - status: - description: ScaledJobStatus defines the observed state of ScaledJob - properties: - conditions: - description: Conditions an array representation to store multiple Conditions - items: - description: Condition to store the condition state - properties: - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition - type: string - required: - - status - - type - type: object - type: array - lastActiveTime: - format: date-time - type: string - type: object - type: object - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.3.0 - creationTimestamp: null - labels: - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: scaledobjects.keda.sh -spec: - additionalPrinterColumns: - - JSONPath: .status.scaleTargetKind - name: ScaleTargetKind - type: string - - JSONPath: .spec.scaleTargetRef.name - name: ScaleTargetName - type: string - - JSONPath: .spec.triggers[*].type - name: Triggers - type: string - - JSONPath: .spec.triggers[*].authenticationRef.name - name: Authentication - type: string - - JSONPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - JSONPath: .status.conditions[?(@.type=="Active")].status - name: Active - type: string - - JSONPath: .metadata.creationTimestamp - name: Age - type: date - group: keda.sh - names: - kind: ScaledObject - listKind: ScaledObjectList - plural: scaledobjects - shortNames: - - so - singular: scaledobject - scope: Namespaced - subresources: - status: {} - validation: - openAPIV3Schema: - description: ScaledObject is a specification for a ScaledObject resource - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ScaledObjectSpec is the spec for a ScaledObject resource - properties: - advanced: - description: AdvancedConfig specifies advance scaling options - properties: - horizontalPodAutoscalerConfig: - description: HorizontalPodAutoscalerConfig specifies horizontal - scale config - properties: - behavior: - description: HorizontalPodAutoscalerBehavior configures the - scaling behavior of the target in both Up and Down directions - (scaleUp and scaleDown fields respectively). - properties: - scaleDown: - description: scaleDown is scaling policy for scaling Down. - If not set, the default value is to allow to scale down - to minReplicas pods, with a 300 second stabilization window - (i.e., the highest recommendation for the last 300sec - is used). - properties: - policies: - description: policies is a list of potential scaling - polices which can be used during scaling. At least - one policy must be specified, otherwise the HPAScalingRules - will be discarded as invalid - items: - description: HPAScalingPolicy is a single policy which - must hold true for a specified past interval. - properties: - periodSeconds: - description: PeriodSeconds specifies the window - of time for which the policy should hold true. - PeriodSeconds must be greater than zero and - less than or equal to 1800 (30 min). - format: int32 - type: integer - type: - description: Type is used to specify the scaling - policy. - type: string - value: - description: Value contains the amount of change - which is permitted by the policy. It must be - greater than zero - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - selectPolicy: - description: selectPolicy is used to specify which policy - should be used. If not set, the default value MaxPolicySelect - is used. - type: string - stabilizationWindowSeconds: - description: 'StabilizationWindowSeconds is the number - of seconds for which past recommendations should be - considered while scaling up or scaling down. StabilizationWindowSeconds - must be greater than or equal to zero and less than - or equal to 3600 (one hour). If not set, use the default - values: - For scale up: 0 (i.e. no stabilization is - done). - For scale down: 300 (i.e. the stabilization - window is 300 seconds long).' - format: int32 - type: integer - type: object - scaleUp: - description: 'scaleUp is scaling policy for scaling Up. - If not set, the default value is the higher of: * increase - no more than 4 pods per 60 seconds * double the number - of pods per 60 seconds No stabilization is used.' - properties: - policies: - description: policies is a list of potential scaling - polices which can be used during scaling. At least - one policy must be specified, otherwise the HPAScalingRules - will be discarded as invalid - items: - description: HPAScalingPolicy is a single policy which - must hold true for a specified past interval. - properties: - periodSeconds: - description: PeriodSeconds specifies the window - of time for which the policy should hold true. - PeriodSeconds must be greater than zero and - less than or equal to 1800 (30 min). - format: int32 - type: integer - type: - description: Type is used to specify the scaling - policy. - type: string - value: - description: Value contains the amount of change - which is permitted by the policy. It must be - greater than zero - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - selectPolicy: - description: selectPolicy is used to specify which policy - should be used. If not set, the default value MaxPolicySelect - is used. - type: string - stabilizationWindowSeconds: - description: 'StabilizationWindowSeconds is the number - of seconds for which past recommendations should be - considered while scaling up or scaling down. StabilizationWindowSeconds - must be greater than or equal to zero and less than - or equal to 3600 (one hour). If not set, use the default - values: - For scale up: 0 (i.e. no stabilization is - done). - For scale down: 300 (i.e. the stabilization - window is 300 seconds long).' - format: int32 - type: integer - type: object - type: object - resourceMetrics: - items: - description: ResourceMetricSource indicates how to scale on - a resource metric known to Kubernetes, as specified in requests - and limits, describing each pod in the current scale target - (e.g. CPU or memory). The values will be averaged together - before being compared to the target. Such metrics are built - in to Kubernetes, and have special scaling options on top - of those available to normal per-pod metrics using the "pods" - source. Only one "target" type should be set. - properties: - name: - description: name is the name of the resource in question. - type: string - target: - description: target specifies the target value for the - given metric - properties: - averageUtilization: - description: averageUtilization is the target value - of the average of the resource metric across all - relevant pods, represented as a percentage of the - requested value of the resource for the pods. Currently - only valid for Resource metric source type - format: int32 - type: integer - averageValue: - anyOf: - - type: integer - - type: string - description: averageValue is the target value of the - average of the metric across all relevant pods (as - a quantity) - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: - description: type represents whether the metric type - is Utilization, Value, or AverageValue - type: string - value: - anyOf: - - type: integer - - type: string - description: value is the target value of the metric - (as a quantity). - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - type - type: object - required: - - name - - target - type: object - type: array - type: object - restoreToOriginalReplicaCount: - type: boolean - type: object - cooldownPeriod: - format: int32 - type: integer - maxReplicaCount: - format: int32 - type: integer - minReplicaCount: - format: int32 - type: integer - pollingInterval: - format: int32 - type: integer - scaleTargetRef: - description: ScaleTarget holds the a reference to the scale target Object - properties: - apiVersion: - type: string - envSourceContainerName: - type: string - kind: - type: string - name: - type: string - required: - - name - type: object - triggers: - items: - description: ScaleTriggers reference the scaler that will be used - properties: - authenticationRef: - description: ScaledObjectAuthRef points to the TriggerAuthentication - object that is used to authenticate the scaler with the environment - properties: - name: - type: string - required: - - name - type: object - metadata: - additionalProperties: - type: string - type: object - name: - type: string - type: - type: string - required: - - metadata - - type - type: object - type: array - required: - - scaleTargetRef - - triggers - type: object - status: - description: ScaledObjectStatus is the status for a ScaledObject resource - properties: - conditions: - description: Conditions an array representation to store multiple Conditions - items: - description: Condition to store the condition state - properties: - message: - description: A human readable message indicating details about - the transition. - type: string - reason: - description: The reason for the condition's last transition. - type: string - status: - description: Status of the condition, one of True, False, Unknown. - type: string - type: - description: Type of condition - type: string - required: - - status - - type - type: object - type: array - externalMetricNames: - items: - type: string - type: array - lastActiveTime: - format: date-time - type: string - originalReplicaCount: - format: int32 - type: integer - scaleTargetGVKR: - description: GroupVersionKindResource provides unified structure for - schema.GroupVersionKind and Resource - properties: - group: - type: string - kind: - type: string - resource: - type: string - version: - type: string - required: - - group - - kind - - resource - - version - type: object - scaleTargetKind: - type: string - type: object - required: - - spec - type: object - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.3.0 - creationTimestamp: null - labels: - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: triggerauthentications.keda.sh -spec: - additionalPrinterColumns: - - JSONPath: .spec.podIdentity.provider - name: PodIdentity - type: string - - JSONPath: .spec.secretTargetRef[*].name - name: Secret - type: string - - JSONPath: .spec.env[*].name - name: Env - type: string - group: keda.sh - names: - kind: TriggerAuthentication - listKind: TriggerAuthenticationList - plural: triggerauthentications - shortNames: - - ta - - triggerauth - singular: triggerauthentication - scope: Namespaced - subresources: {} - validation: - openAPIV3Schema: - description: TriggerAuthentication defines how a trigger can authenticate - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: TriggerAuthenticationSpec defines the various ways to authenticate - properties: - env: - items: - description: AuthEnvironment is used to authenticate using environment - variables in the destination ScaleTarget spec - properties: - containerName: - type: string - name: - type: string - parameter: - type: string - required: - - name - - parameter - type: object - type: array - hashiCorpVault: - description: HashiCorpVault is used to authenticate using Hashicorp - Vault - properties: - address: - type: string - authentication: - description: VaultAuthentication contains the list of Hashicorp - Vault authentication methods - type: string - credential: - description: Credential defines the Hashicorp Vault credentials - depending on the authentication method - properties: - serviceAccount: - type: string - token: - type: string - type: object - mount: - type: string - role: - type: string - secrets: - items: - description: VaultSecret defines the mapping between the path - of the secret in Vault to the parameter - properties: - key: - type: string - parameter: - type: string - path: - type: string - required: - - key - - parameter - - path - type: object - type: array - required: - - address - - authentication - - secrets - type: object - podIdentity: - description: AuthPodIdentity allows users to select the platform native - identity mechanism - properties: - provider: - description: PodIdentityProvider contains the list of providers - type: string - required: - - provider - type: object - secretTargetRef: - items: - description: AuthSecretTargetRef is used to authenticate using a reference - to a secret - properties: - key: - type: string - name: - type: string - parameter: - type: string - required: - - key - - name - - parameter - type: object - type: array - type: object - required: - - spec - type: object - version: v1alpha1 - versions: - - name: v1alpha1 - served: true - storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/name: keda-operator - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-operator - namespace: keda ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: keda-external-metrics-reader - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-external-metrics-reader -rules: -- apiGroups: - - external.metrics.k8s.io - resources: - - '*' - verbs: - - '*' ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - labels: - app.kubernetes.io/name: keda-operator - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-operator -rules: -- apiGroups: - - "" - resources: - - configmaps - - configmaps/status - - events - verbs: - - '*' -- apiGroups: - - "" - resources: - - external - - pods - - secrets - - services - verbs: - - get - - list - - watch -- apiGroups: - - '*' - resources: - - '*' - verbs: - - get -- apiGroups: - - '*' - resources: - - '*/scale' - verbs: - - '*' -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - '*' -- apiGroups: - - batch - resources: - - jobs - verbs: - - '*' -- apiGroups: - - keda.sh - resources: - - scaledjobs - - scaledjobs/status - verbs: - - '*' -- apiGroups: - - keda.sh - resources: - - scaledobjects - - scaledobjects/finalizers - - scaledobjects/status - verbs: - - '*' -- apiGroups: - - keda.sh - resources: - - triggerauthentications - - triggerauthentications/status - verbs: - - '*' ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/name: keda-auth-reader - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-auth-reader - namespace: keda -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: extension-apiserver-authentication-reader -subjects: -- kind: ServiceAccount - name: keda-operator - namespace: keda ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: keda-hpa-controller-external-metrics - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-hpa-controller-external-metrics -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: keda-external-metrics-reader -subjects: -- kind: ServiceAccount - name: horizontal-pod-autoscaler - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: keda-operator - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-operator -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: keda-operator -subjects: -- kind: ServiceAccount - name: keda-operator - namespace: keda ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: keda-system-auth-delegator - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda:system:auth-delegator -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: keda-operator - namespace: keda ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: keda-metrics-apiserver - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-metrics-apiserver - namespace: keda -spec: - ports: - - name: https - port: 443 - targetPort: 6443 - - name: http - port: 80 - targetPort: 8080 - selector: - app: keda-metrics-apiserver - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: keda-metrics-apiserver - app.kubernetes.io/name: keda-metrics-apiserver - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-metrics-apiserver - namespace: keda -spec: - replicas: 1 - selector: - matchLabels: - app: keda-metrics-apiserver - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - template: - metadata: - labels: - app: keda-metrics-apiserver - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-metrics-apiserver - spec: - containers: - - args: - - /usr/local/bin/keda-adapter - - --secure-port=6443 - - --logtostderr=true - - --v=4 - env: - - name: WATCH_NAMESPACE - value: "" - image: docker.io/kedacore/keda-metrics-apiserver:2.0.0-beta - imagePullPolicy: Always - livenessProbe: - httpGet: - path: /healthz - port: 6443 - scheme: HTTPS - initialDelaySeconds: 5 - name: keda-metrics-apiserver - ports: - - containerPort: 6443 - name: https - - containerPort: 8080 - name: http - readinessProbe: - httpGet: - path: /readyz - port: 6443 - scheme: HTTPS - initialDelaySeconds: 5 - resources: - limits: - cpu: 1000m - memory: 1000Mi - requests: - cpu: 100m - memory: 100Mi - volumeMounts: - - mountPath: /tmp - name: temp-vol - nodeSelector: - beta.kubernetes.io/os: linux - serviceAccountName: keda-operator - volumes: - - emptyDir: {} - name: temp-vol ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: keda-operator - app.kubernetes.io/component: operator - app.kubernetes.io/name: keda-operator - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-operator - namespace: keda -spec: - replicas: 1 - selector: - matchLabels: - app: keda-operator - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - template: - metadata: - labels: - app: keda-operator - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: keda-operator - name: keda-operator - spec: - containers: - - args: - - --enable-leader-election - - --zap-log-level=debug - - --zap-encoder=console - command: - - /keda - env: - - name: WATCH_NAMESPACE - value: "" - image: docker.io/kedacore/keda:2.0.0-beta - imagePullPolicy: Always - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 25 - name: keda-operator - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 20 - resources: - limits: - cpu: 1000m - memory: 1000Mi - requests: - cpu: 100m - memory: 100Mi - nodeSelector: - beta.kubernetes.io/os: linux - serviceAccountName: keda-operator - terminationGracePeriodSeconds: 10 ---- -apiVersion: apiregistration.k8s.io/v1 -kind: APIService -metadata: - labels: - app.kubernetes.io/name: v1beta1.external.metrics.k8s.io - app.kubernetes.io/part-of: keda-operator - app.kubernetes.io/version: 2.0.0-beta - name: v1beta1.external.metrics.k8s.io -spec: - group: external.metrics.k8s.io - groupPriorityMinimum: 100 - insecureSkipTLSVerify: true - service: - name: keda-metrics-apiserver - namespace: keda - version: v1beta1 - versionPriority: 100 +apiVersion: v1 +kind: Namespace +metadata: + labels: + app.kubernetes.io/name: keda + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + labels: + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: scaledjobs.keda.sh +spec: + additionalPrinterColumns: + - JSONPath: .spec.triggers[*].type + name: Triggers + type: string + - JSONPath: .spec.triggers[*].authenticationRef.name + name: Authentication + type: string + - JSONPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - JSONPath: .status.conditions[?(@.type=="Active")].status + name: Active + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: keda.sh + names: + kind: ScaledJob + listKind: ScaledJobList + plural: scaledjobs + shortNames: + - sj + singular: scaledjob + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: ScaledJob is the Schema for the scaledjobs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ScaledJobSpec defines the desired state of ScaledJob + properties: + envSourceContainerName: + type: string + failedJobsHistoryLimit: + format: int32 + type: integer + jobTargetRef: + description: JobSpec describes how the job execution will look like. + properties: + activeDeadlineSeconds: + description: Specifies the duration in seconds relative to the startTime + that the job may be active before the system tries to terminate + it; value must be positive integer + format: int64 + type: integer + backoffLimit: + description: Specifies the number of retries before marking this + job failed. Defaults to 6 + format: int32 + type: integer + completions: + description: 'Specifies the desired number of successfully finished + pods the job should be run with. Setting to nil means that the + success of any pod signals the success of all pods, and allows + parallelism to have any positive value. Setting to 1 means that + parallelism is limited to 1 and the success of that pod signals + the success of the job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/' + format: int32 + type: integer + manualSelector: + description: 'manualSelector controls generation of pod labels and + pod selectors. Leave `manualSelector` unset unless you are certain + what you are doing. When false or unset, the system pick labels + unique to this job and appends those labels to the pod template. When + true, the user is responsible for picking unique labels and specifying + the selector. Failure to pick a unique label may cause this and + other jobs to not function correctly. However, You may see `manualSelector=true` + in jobs that were created with the old `extensions/v1beta1` API. + More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector' + type: boolean + parallelism: + description: 'Specifies the maximum desired number of pods the job + should run at any given time. The actual number of pods running + in steady state will be less than this number when ((.spec.completions + - .status.successful) < .spec.parallelism), i.e. when the work + left to do is less than max parallelism. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/' + format: int32 + type: integer + selector: + description: 'A label query over pods that should match the pod + count. Normally, the system sets this field for you. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors' + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + template: + description: 'Describes the pod that will be created when executing + a job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/' + properties: + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: 'Specification of the desired behavior of the pod. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + activeDeadlineSeconds: + description: Optional duration in seconds the pod may be + active on the node relative to StartTime before the system + will actively try to mark it failed and kill associated + containers. Value must be a positive integer. + format: int64 + type: integer + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules + for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. + The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node + that meets all of the scheduling requirements + (resource request, requiredDuringScheduling affinity + expressions, etc.), compute a sum by iterating + through the elements of this field and adding + "weight" to the sum if the node matches the corresponding + matchExpressions; the node(s) with the highest + sum are the most preferred. + items: + description: An empty preferred scheduling term + matches all objects with implicit weight 0 (i.e. + it's a no-op). A null preferred scheduling term + matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated + with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. If + the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. If + the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching + the corresponding nodeSelectorTerm, in the + range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, + the pod will not be scheduled onto the node. If + the affinity requirements specified by this field + cease to be met at some point during pod execution + (e.g. due to an update), the system may or may + not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector + terms. The terms are ORed. + items: + description: A null or empty node selector + term matches no objects. The requirements + of them are ANDed. The TopologySelectorTerm + type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. If + the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. If + the operator is Gt or Lt, the + values array must have a single + element, which will be interpreted + as an integer. This array is replaced + during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules + (e.g. co-locate this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. + The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node + that meets all of the scheduling requirements + (resource request, requiredDuringScheduling affinity + expressions, etc.), compute a sum by iterating + through the elements of this field and adding + "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched + WeightedPodAffinityTerm fields are added per-node + to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set + of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a + list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a + set of values. Valid operators + are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values + array must be non-empty. If + the operator is Exists or + DoesNotExist, the values array + must be empty. This array + is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map + of {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which + namespaces the labelSelector applies + to (matches against); null or empty + list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in the + range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, + the pod will not be scheduled onto the node. If + the affinity requirements specified by this field + cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may + or may not try to eventually evict the pod from + its node. When there are multiple elements, the + lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the given + namespace(s)) that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose value + of the label with key topologyKey matches + that of any node on which any of the selected + pods is running. Empty topologyKey is not + allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling + rules (e.g. avoid putting this pod in the same node, + zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node + that violates one or more of the expressions. + The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node + that meets all of the scheduling requirements + (resource request, requiredDuringScheduling anti-affinity + expressions, etc.), compute a sum by iterating + through the elements of this field and adding + "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched + WeightedPodAffinityTerm fields are added per-node + to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set + of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a + list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a + set of values. Valid operators + are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values + array must be non-empty. If + the operator is Exists or + DoesNotExist, the values array + must be empty. This array + is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map + of {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which + namespaces the labelSelector applies + to (matches against); null or empty + list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in the + range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, + the pod will not be scheduled onto the node. If + the anti-affinity requirements specified by this + field cease to be met at some point during pod + execution (e.g. due to a pod label update), the + system may or may not try to eventually evict + the pod from its node. When there are multiple + elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all + terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the given + namespace(s)) that this pod should be co-located + (affinity) or not co-located (anti-affinity) + with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose value + of the label with key topologyKey matches + that of any node on which any of the selected + pods is running. Empty topologyKey is not + allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether + a service account token should be automatically mounted. + type: boolean + containers: + description: List of containers belonging to the pod. Containers + cannot currently be added or removed. There must be at + least one container in a Pod. Cannot be updated. + items: + description: A single application container that you want + to run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker + image''s CMD is used if this is not provided. Variable + references $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, the + reference in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped with a double + $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within + a shell. The docker image''s ENTRYPOINT is used + if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. The $(VAR_NAME) + syntax can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be + updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set + in the container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. + Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previous defined environment + variables in the container and any service + environment variables. If a variable cannot + be resolved, the reference in the input string + will be unchanged. The $(VAR_NAME) syntax + can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: + supports metadata.name, metadata.namespace, + metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in + the pod's namespace + properties: + key: + description: The key of the secret to + select from. Must be a valid secret + key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined within + a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container + is starting. When a key exists in multiple sources, + the value associated with the last source will take + precedence. Values defined by an Env with a duplicate + key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config + management to default or override container images + in workload controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, + IfNotPresent. Defaults to Always if :latest tag + is specified, or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should + take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the handler + fails, the container is terminated and restarted + according to its restart policy. Other management + of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the + working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to explicitly + call out to that shell. Exit status + of 0 is treated as live/healthy and + non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a + custom header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic TCP + lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before + a container is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource contention, + etc. The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop + hooked is executed. Regardless of the outcome + of the handler, the container will eventually + terminate within the Pod''s termination grace + period. Other management of the container blocks + until the hook completes or until the termination + grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the + working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to explicitly + call out to that shell. Exit status + of 0 is treated as live/healthy and + non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a + custom header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic TCP + lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a + DNS_LABEL. Each container in a pod must have a unique + name (DNS_LABEL). Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional + information about the network connections a container + uses, but is primarily informational. Not specifying + a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default + "0.0.0.0" address inside a container will be accessible + from the network. Cannot be updated. + items: + description: ContainerPort represents a network + port in a single container. + properties: + containerPort: + description: Number of port to expose on the + pod's IP address. This must be a valid port + number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external + port to. + type: string + hostPort: + description: Number of port to expose on the + host. If specified, this must be a valid port + number, 0 < x < 65536. If HostNetwork is specified, + this must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port + in a pod must have a unique name. Name for + the port that can be referred to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, + TCP, or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + - protocol + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service + readiness. Container will be removed from service + endpoints if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is + omitted for a container, it defaults to Limits + if that is explicitly specified, otherwise to + an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: 'Security options the pod should run + with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls + whether a process can gain more privileges than + its parent process. This bool directly controls + if the no_new_privs flag will be set on the + container process. AllowPrivilegeEscalation + is true always when the container is: 1) run + as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when + running containers. Defaults to the default + set of capabilities granted by the container + runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. + Processes in privileged containers are essentially + equivalent to root on the host. Defaults to + false. + type: boolean + procMount: + description: procMount denotes the type of proc + mount to use for the containers. The default + is DefaultProcMount which uses the container + runtime defaults for readonly paths and masked + paths. This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of + the container process. Uses runtime default + if unset. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must + run as a non-root user. If true, the Kubelet + will validate the image at runtime to ensure + that it does not run as UID 0 (root) and fail + to start the container if it does. If unset + or false, no such validation will be performed. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of + the container process. Defaults to user specified + in image metadata if unspecified. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied + to the container. If unspecified, the container + runtime will allocate a random SELinux context + for each container. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + level: + description: Level is SELinux level label + that applies to the container. + type: string + role: + description: Role is a SELinux role label + that applies to the container. + type: string + type: + description: Type is a SELinux type label + that applies to the container. + type: string + user: + description: User is a SELinux user label + that applies to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options + from the PodSecurityContext will be used. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the + GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the + name of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run + the entrypoint of the container process. + Defaults to the user specified in image + metadata if unspecified. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod + has successfully initialized. If specified, no other + probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, + just as if the livenessProbe failed. This can be + used to provide different probe parameters at the + beginning of a Pod''s lifecycle, when it might take + a long time to load data or warm a cache, than during + steady-state operation. This cannot be updated. + This is a beta feature enabled by the StartupProbe + feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate + a buffer for stdin in the container runtime. If + this is not set, reads from stdin in the container + will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should + close the stdin channel after it has been opened + by a single attach. When stdin is true the stdin + stream will remain open across multiple attach sessions. + If stdinOnce is set to true, stdin is opened on + container start, is empty until the first client + attaches to stdin, and then remains open and accepts + data until the client disconnects, at which time + stdin is closed and remains closed until the container + is restarted. If this flag is false, a container + processes that reads from stdin will never receive + an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to + which the container''s termination message will + be written is mounted into the container''s filesystem. + Message written is intended to be brief final status, + such as an assertion failure message. Will be truncated + by the node if greater than 4096 bytes. The total + message length across all containers will be limited + to 12kb. Defaults to /dev/termination-log. Cannot + be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message + should be populated. File will use the contents + of terminationMessagePath to populate the container + status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output + if the termination message file is empty and the + container exited with an error. The log output is + limited to 2048 bytes or 80 lines, whichever is + smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate + a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of + a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside of + the container that the device will be mapped + to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's + filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting of + a Volume within a container. + properties: + mountPath: + description: Path within the container at which + the volume should be mounted. Must not contain + ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should be + mounted. Behaves similarly to SubPath but + environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not + specified, the container runtime's default will + be used, which might be configured in the container + image. Cannot be updated. + type: string + required: + - name + type: object + type: array + dnsConfig: + description: Specifies the DNS parameters of a pod. Parameters + specified here will be merged to the generated DNS configuration + based on DNSPolicy. + properties: + nameservers: + description: A list of DNS name server IP addresses. + This will be appended to the base nameservers generated + from DNSPolicy. Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: A list of DNS resolver options. This will + be merged with the base options generated from DNSPolicy. + Duplicated entries will be removed. Resolution options + given in Options will override those that appear in + the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver + options of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: A list of DNS search domains for host-name + lookup. This will be appended to the base search paths + generated from DNSPolicy. Duplicated search paths + will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: Set DNS policy for the pod. Defaults to "ClusterFirst". + Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', + 'Default' or 'None'. DNS parameters given in DNSConfig + will be merged with the policy selected with DNSPolicy. + To have DNS options set along with hostNetwork, you have + to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. + type: string + enableServiceLinks: + description: 'EnableServiceLinks indicates whether information + about services should be injected into pod''s environment + variables, matching the syntax of Docker links. Optional: + Defaults to true.' + type: boolean + ephemeralContainers: + description: List of ephemeral containers run in this pod. + Ephemeral containers may be run in an existing pod to + perform user-initiated actions such as debugging. This + list cannot be specified when creating a pod, and it cannot + be modified by updating the pod spec. In order to add + an ephemeral container to an existing pod, use the pod's + ephemeralcontainers subresource. This field is alpha-level + and is only honored by servers that enable the EphemeralContainers + feature. + items: + description: An EphemeralContainer is a container that + may be added temporarily to an existing pod for user-initiated + activities such as debugging. Ephemeral containers have + no resource or scheduling guarantees, and they will + not be restarted when they exit or when a pod is removed + or restarted. If an ephemeral container causes a pod + to exceed its resource allocation, the pod may be evicted. + Ephemeral containers may not be added by directly updating + the pod spec. They must be added via the pod's ephemeralcontainers + subresource, and they will appear in the pod spec once + added. This is an alpha feature enabled by the EphemeralContainers + feature flag. + properties: + args: + description: 'Arguments to the entrypoint. The docker + image''s CMD is used if this is not provided. Variable + references $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, the + reference in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped with a double + $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within + a shell. The docker image''s ENTRYPOINT is used + if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. The $(VAR_NAME) + syntax can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be + updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set + in the container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. + Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previous defined environment + variables in the container and any service + environment variables. If a variable cannot + be resolved, the reference in the input string + will be unchanged. The $(VAR_NAME) syntax + can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: + supports metadata.name, metadata.namespace, + metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in + the pod's namespace + properties: + key: + description: The key of the secret to + select from. Must be a valid secret + key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined within + a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container + is starting. When a key exists in multiple sources, + the value associated with the last source will take + precedence. Values defined by an Env with a duplicate + key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, + IfNotPresent. Defaults to Always if :latest tag + is specified, or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Lifecycle is not allowed for ephemeral + containers. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the handler + fails, the container is terminated and restarted + according to its restart policy. Other management + of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the + working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to explicitly + call out to that shell. Exit status + of 0 is treated as live/healthy and + non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a + custom header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic TCP + lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before + a container is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource contention, + etc. The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop + hooked is executed. Regardless of the outcome + of the handler, the container will eventually + terminate within the Pod''s termination grace + period. Other management of the container blocks + until the hook completes or until the termination + grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the + working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to explicitly + call out to that shell. Exit status + of 0 is treated as live/healthy and + non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a + custom header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic TCP + lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the ephemeral container specified + as a DNS_LABEL. This name must be unique among all + containers, init containers and ephemeral containers. + type: string + ports: + description: Ports are not allowed for ephemeral containers. + items: + description: ContainerPort represents a network + port in a single container. + properties: + containerPort: + description: Number of port to expose on the + pod's IP address. This must be a valid port + number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external + port to. + type: string + hostPort: + description: Number of port to expose on the + host. If specified, this must be a valid port + number, 0 < x < 65536. If HostNetwork is specified, + this must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port + in a pod must have a unique name. Name for + the port that can be referred to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, + TCP, or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + - protocol + type: object + type: array + readinessProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: Resources are not allowed for ephemeral + containers. Ephemeral containers use spare resources + already allocated to the pod. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is + omitted for a container, it defaults to Limits + if that is explicitly specified, otherwise to + an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: SecurityContext is not allowed for ephemeral + containers. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls + whether a process can gain more privileges than + its parent process. This bool directly controls + if the no_new_privs flag will be set on the + container process. AllowPrivilegeEscalation + is true always when the container is: 1) run + as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when + running containers. Defaults to the default + set of capabilities granted by the container + runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. + Processes in privileged containers are essentially + equivalent to root on the host. Defaults to + false. + type: boolean + procMount: + description: procMount denotes the type of proc + mount to use for the containers. The default + is DefaultProcMount which uses the container + runtime defaults for readonly paths and masked + paths. This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of + the container process. Uses runtime default + if unset. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must + run as a non-root user. If true, the Kubelet + will validate the image at runtime to ensure + that it does not run as UID 0 (root) and fail + to start the container if it does. If unset + or false, no such validation will be performed. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of + the container process. Defaults to user specified + in image metadata if unspecified. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied + to the container. If unspecified, the container + runtime will allocate a random SELinux context + for each container. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + level: + description: Level is SELinux level label + that applies to the container. + type: string + role: + description: Role is a SELinux role label + that applies to the container. + type: string + type: + description: Type is a SELinux type label + that applies to the container. + type: string + user: + description: User is a SELinux user label + that applies to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options + from the PodSecurityContext will be used. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the + GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the + name of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run + the entrypoint of the container process. + Defaults to the user specified in image + metadata if unspecified. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate + a buffer for stdin in the container runtime. If + this is not set, reads from stdin in the container + will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should + close the stdin channel after it has been opened + by a single attach. When stdin is true the stdin + stream will remain open across multiple attach sessions. + If stdinOnce is set to true, stdin is opened on + container start, is empty until the first client + attaches to stdin, and then remains open and accepts + data until the client disconnects, at which time + stdin is closed and remains closed until the container + is restarted. If this flag is false, a container + processes that reads from stdin will never receive + an EOF. Default is false + type: boolean + targetContainerName: + description: If set, the name of the container from + PodSpec that this ephemeral container targets. The + ephemeral container will be run in the namespaces + (IPC, PID, etc) of this container. If not set then + the ephemeral container is run in whatever namespaces + are shared for the pod. Note that the container + runtime must support this feature. + type: string + terminationMessagePath: + description: 'Optional: Path at which the file to + which the container''s termination message will + be written is mounted into the container''s filesystem. + Message written is intended to be brief final status, + such as an assertion failure message. Will be truncated + by the node if greater than 4096 bytes. The total + message length across all containers will be limited + to 12kb. Defaults to /dev/termination-log. Cannot + be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message + should be populated. File will use the contents + of terminationMessagePath to populate the container + status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output + if the termination message file is empty and the + container exited with an error. The log output is + limited to 2048 bytes or 80 lines, whichever is + smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate + a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of + a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside of + the container that the device will be mapped + to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's + filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting of + a Volume within a container. + properties: + mountPath: + description: Path within the container at which + the volume should be mounted. Must not contain + ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should be + mounted. Behaves similarly to SubPath but + environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not + specified, the container runtime's default will + be used, which might be configured in the container + image. Cannot be updated. + type: string + required: + - name + type: object + type: array + hostAliases: + description: HostAliases is an optional list of hosts and + IPs that will be injected into the pod's hosts file if + specified. This is only valid for non-hostNetwork pods. + items: + description: HostAlias holds the mapping between IP and + hostnames that will be injected as an entry in the pod's + hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + ip: + description: IP address of the host file entry. + type: string + type: object + type: array + hostIPC: + description: 'Use the host''s ipc namespace. Optional: Default + to false.' + type: boolean + hostNetwork: + description: Host networking requested for this pod. Use + the host's network namespace. If this option is set, the + ports that will be used must be specified. Default to + false. + type: boolean + hostPID: + description: 'Use the host''s pid namespace. Optional: Default + to false.' + type: boolean + hostname: + description: Specifies the hostname of the Pod If not specified, + the pod's hostname will be set to a system-defined value. + type: string + imagePullSecrets: + description: 'ImagePullSecrets is an optional list of references + to secrets in the same namespace to use for pulling any + of the images used by this PodSpec. If specified, these + secrets will be passed to individual puller implementations + for them to use. For example, in the case of docker, only + DockerConfig type secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same + namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + type: array + initContainers: + description: 'List of initialization containers belonging + to the pod. Init containers are executed in order prior + to containers being started. If any init container fails, + the pod is considered to have failed and is handled according + to its restartPolicy. The name for an init container or + normal container must be unique among all containers. + Init containers may not have Lifecycle actions, Readiness + probes, Liveness probes, or Startup probes. The resourceRequirements + of an init container are taken into account during scheduling + by finding the highest request/limit for each resource + type, and then using the max of of that value or the sum + of the normal containers. Limits are applied to init containers + in a similar fashion. Init containers cannot currently + be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + items: + description: A single application container that you want + to run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker + image''s CMD is used if this is not provided. Variable + references $(VAR_NAME) are expanded using the container''s + environment. If a variable cannot be resolved, the + reference in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped with a double + $$, ie: $$(VAR_NAME). Escaped references will never + be expanded, regardless of whether the variable + exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within + a shell. The docker image''s ENTRYPOINT is used + if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. The $(VAR_NAME) + syntax can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be + updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set + in the container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. + Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previous defined environment + variables in the container and any service + environment variables. If a variable cannot + be resolved, the reference in the input string + will be unchanged. The $(VAR_NAME) syntax + can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, + regardless of whether the variable exists + or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: + supports metadata.name, metadata.namespace, + metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, + requests.cpu, requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in + the pod's namespace + properties: + key: + description: The key of the secret to + select from. Must be a valid secret + key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined within + a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container + is starting. When a key exists in multiple sources, + the value associated with the last source will take + precedence. Values defined by an Env with a duplicate + key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config + management to default or override container images + in workload controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, + IfNotPresent. Defaults to Always if :latest tag + is specified, or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should + take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the handler + fails, the container is terminated and restarted + according to its restart policy. Other management + of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the + working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to explicitly + call out to that shell. Exit status + of 0 is treated as live/healthy and + non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a + custom header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic TCP + lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before + a container is terminated due to an API request + or management event such as liveness/startup + probe failure, preemption, resource contention, + etc. The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop + hooked is executed. Regardless of the outcome + of the handler, the container will eventually + terminate within the Pod''s termination grace + period. Other management of the container blocks + until the hook completes or until the termination + grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command line + to execute inside the container, the + working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to explicitly + call out to that shell. Exit status + of 0 is treated as live/healthy and + non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a + custom header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic TCP + lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a + DNS_LABEL. Each container in a pod must have a unique + name (DNS_LABEL). Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional + information about the network connections a container + uses, but is primarily informational. Not specifying + a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default + "0.0.0.0" address inside a container will be accessible + from the network. Cannot be updated. + items: + description: ContainerPort represents a network + port in a single container. + properties: + containerPort: + description: Number of port to expose on the + pod's IP address. This must be a valid port + number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external + port to. + type: string + hostPort: + description: Number of port to expose on the + host. If specified, this must be a valid port + number, 0 < x < 65536. If HostNetwork is specified, + this must match ContainerPort. Most containers + do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port + in a pod must have a unique name. Name for + the port that can be referred to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, + TCP, or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + - protocol + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service + readiness. Container will be removed from service + endpoints if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is + omitted for a container, it defaults to Limits + if that is explicitly specified, otherwise to + an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: 'Security options the pod should run + with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls + whether a process can gain more privileges than + its parent process. This bool directly controls + if the no_new_privs flag will be set on the + container process. AllowPrivilegeEscalation + is true always when the container is: 1) run + as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when + running containers. Defaults to the default + set of capabilities granted by the container + runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. + Processes in privileged containers are essentially + equivalent to root on the host. Defaults to + false. + type: boolean + procMount: + description: procMount denotes the type of proc + mount to use for the containers. The default + is DefaultProcMount which uses the container + runtime defaults for readonly paths and masked + paths. This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of + the container process. Uses runtime default + if unset. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must + run as a non-root user. If true, the Kubelet + will validate the image at runtime to ensure + that it does not run as UID 0 (root) and fail + to start the container if it does. If unset + or false, no such validation will be performed. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of + the container process. Defaults to user specified + in image metadata if unspecified. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied + to the container. If unspecified, the container + runtime will allocate a random SELinux context + for each container. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + level: + description: Level is SELinux level label + that applies to the container. + type: string + role: + description: Role is a SELinux role label + that applies to the container. + type: string + type: + description: Type is a SELinux type label + that applies to the container. + type: string + user: + description: User is a SELinux user label + that applies to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options + from the PodSecurityContext will be used. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the + GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the + name of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run + the entrypoint of the container process. + Defaults to the user specified in image + metadata if unspecified. May also be set + in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified + in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod + has successfully initialized. If specified, no other + probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, + just as if the livenessProbe failed. This can be + used to provide different probe parameters at the + beginning of a Pod''s lifecycle, when it might take + a long time to load data or warm a cache, than during + steady-state operation. This cannot be updated. + This is a beta feature enabled by the StartupProbe + feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside a + shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you + need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum value + is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action involving + a TCP port. TCP hooks not yet supported TODO: + implement a realistic TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to + access on the container. Number must be + in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which the + probe times out. Defaults to 1 second. Minimum + value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate + a buffer for stdin in the container runtime. If + this is not set, reads from stdin in the container + will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should + close the stdin channel after it has been opened + by a single attach. When stdin is true the stdin + stream will remain open across multiple attach sessions. + If stdinOnce is set to true, stdin is opened on + container start, is empty until the first client + attaches to stdin, and then remains open and accepts + data until the client disconnects, at which time + stdin is closed and remains closed until the container + is restarted. If this flag is false, a container + processes that reads from stdin will never receive + an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to + which the container''s termination message will + be written is mounted into the container''s filesystem. + Message written is intended to be brief final status, + such as an assertion failure message. Will be truncated + by the node if greater than 4096 bytes. The total + message length across all containers will be limited + to 12kb. Defaults to /dev/termination-log. Cannot + be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message + should be populated. File will use the contents + of terminationMessagePath to populate the container + status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output + if the termination message file is empty and the + container exited with an error. The log output is + limited to 2048 bytes or 80 lines, whichever is + smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate + a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of + a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside of + the container that the device will be mapped + to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's + filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting of + a Volume within a container. + properties: + mountPath: + description: Path within the container at which + the volume should be mounted. Must not contain + ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should be + mounted. Behaves similarly to SubPath but + environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not + specified, the container runtime's default will + be used, which might be configured in the container + image. Cannot be updated. + type: string + required: + - name + type: object + type: array + nodeName: + description: NodeName is a request to schedule this pod + onto a specific node. If it is non-empty, the scheduler + simply schedules this pod onto that node, assuming that + it fits resource requirements. + type: string + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true + for the pod to fit on a node. Selector which must match + a node''s labels for the pod to be scheduled on that node. + More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Overhead represents the resource overhead + associated with running a pod for a given RuntimeClass. + This field will be autopopulated at admission time by + the RuntimeClass admission controller. If the RuntimeClass + admission controller is enabled, overhead must not be + set in Pod create requests. The RuntimeClass admission + controller will reject Pod create requests which have + the overhead already set. If RuntimeClass is configured + and selected in the PodSpec, Overhead will be set to the + value defined in the corresponding RuntimeClass, otherwise + it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md + This field is alpha-level as of Kubernetes v1.16, and + is only honored by servers that enable the PodOverhead + feature.' + type: object + preemptionPolicy: + description: PreemptionPolicy is the Policy for preempting + pods with lower priority. One of Never, PreemptLowerPriority. + Defaults to PreemptLowerPriority if unset. This field + is alpha-level and is only honored by servers that enable + the NonPreemptingPriority feature. + type: string + priority: + description: The priority value. Various system components + use this field to find the priority of the pod. When Priority + Admission Controller is enabled, it prevents users from + setting this field. The admission controller populates + this field from PriorityClassName. The higher the value, + the higher the priority. + format: int32 + type: integer + priorityClassName: + description: If specified, indicates the pod's priority. + "system-node-critical" and "system-cluster-critical" are + two special keywords which indicate the highest priorities + with the former being the highest priority. Any other + name must be defined by creating a PriorityClass object + with that name. If not specified, the pod priority will + be default or zero if there is no default. + type: string + readinessGates: + description: 'If specified, all readiness gates will be + evaluated for pod readiness. A pod is ready when all its + containers are ready AND all conditions specified in the + readiness gates have status equal to "True" More info: + https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md' + items: + description: PodReadinessGate contains the reference to + a pod condition + properties: + conditionType: + description: ConditionType refers to a condition in + the pod's condition list with matching type. + type: string + required: + - conditionType + type: object + type: array + restartPolicy: + description: 'Restart policy for all containers within the + pod. One of Always, OnFailure, Never. Default to Always. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + type: string + runtimeClassName: + description: 'RuntimeClassName refers to a RuntimeClass + object in the node.k8s.io group, which should be used + to run this pod. If no RuntimeClass resource matches + the named class, the pod will not be run. If unset or + empty, the "legacy" RuntimeClass will be used, which is + an implicit class with an empty definition that uses the + default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + This is a beta feature as of Kubernetes v1.14.' + type: string + schedulerName: + description: If specified, the pod will be dispatched by + specified scheduler. If not specified, the pod will be + dispatched by default scheduler. + type: string + securityContext: + description: 'SecurityContext holds pod-level security attributes + and common container settings. Optional: Defaults to empty. See + type description for default values of each field.' + properties: + fsGroup: + description: "A special supplemental group that applies + to all containers in a pod. Some volume types allow + the Kubelet to change the ownership of that volume + to be owned by the pod: \n 1. The owning GID will + be the FSGroup 2. The setgid bit is set (new files + created in the volume will be owned by FSGroup) 3. + The permission bits are OR'd with rw-rw---- \n If + unset, the Kubelet will not modify the ownership and + permissions of any volume." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of + changing ownership and permission of the volume before + being exposed inside Pod. This field will only apply + to volume types which support fsGroup based ownership(and + permissions). It will have no effect on ephemeral + volume types such as: secret, configmaps and emptydir. + Valid values are "OnRootMismatch" and "Always". If + not specified defaults to "Always".' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be + set in SecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. If true, the Kubelet will validate + the image at runtime to ensure that it does not run + as UID 0 (root) and fail to start the container if + it does. If unset or false, no such validation will + be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all + containers. If unspecified, the container runtime + will allocate a random SELinux context for each container. May + also be set in SecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's + primary GID. If unspecified, no groups will be added + to any container. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls + used for the pod. Pods with unsupported sysctls (by + the container runtime) might fail to launch. + items: + description: Sysctl defines a kernel parameter to + be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to + all containers. If unspecified, the options within + a container's SecurityContext will be used. If set + in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA + admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec + named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the + entrypoint of the container process. Defaults + to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + serviceAccount: + description: 'DeprecatedServiceAccount is a depreciated + alias for ServiceAccountName. Deprecated: Use serviceAccountName + instead.' + type: string + serviceAccountName: + description: 'ServiceAccountName is the name of the ServiceAccount + to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + type: string + shareProcessNamespace: + description: 'Share a single process namespace between all + of the containers in a pod. When this is set containers + will be able to view and signal processes from other containers + in the same pod, and the first process in each container + will not be assigned PID 1. HostPID and ShareProcessNamespace + cannot both be set. Optional: Default to false.' + type: boolean + subdomain: + description: If specified, the fully qualified Pod hostname + will be "...svc.". If not specified, the pod will not have a domainname + at all. + type: string + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully. May be decreased in delete request. + Value must be non-negative integer. The value zero indicates + delete immediately. If this value is nil, the default + grace period will be used instead. The grace period is + the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for + your process. Defaults to 30 seconds. + format: int64 + type: integer + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple + using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to + match. Empty means match all taint effects. When + specified, allowed values are NoSchedule, PreferNoSchedule + and NoExecute. + type: string + key: + description: Key is the taint key that the toleration + applies to. Empty means match all taint keys. If + the key is empty, operator must be Exists; this + combination means to match all values and all keys. + type: string + operator: + description: Operator represents a key's relationship + to the value. Valid operators are Exists and Equal. + Defaults to Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate all taints + of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period + of time the toleration (which must be of effect + NoExecute, otherwise this field is ignored) tolerates + the taint. By default, it is not set, which means + tolerate the taint forever (do not evict). Zero + and negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration + matches to. If the operator is Exists, the value + should be empty, otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints describes how a group + of pods ought to spread across topology domains. Scheduler + will schedule pods in a way which abides by the constraints. + This field is only honored by clusters that enable the + EvenPodsSpread feature. All topologySpreadConstraints + are ANDed. + items: + description: TopologySpreadConstraint specifies how to + spread matching pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching + pods. Pods that match this label selector are counted + to determine the number of pods in their corresponding + topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which + pods may be unevenly distributed. It''s the maximum + permitted difference between the number of matching + pods in any two topology domains of a given topology + type. For example, in a 3-zone cluster, MaxSkew + is set to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can only be scheduled + to zone3 to become 1/1/1; scheduling it onto zone1(zone2) + would make the ActualSkew(2-0) on zone1(zone2) violate + MaxSkew(1). - if MaxSkew is 2, incoming pod can + be scheduled onto any zone. It''s a required field. + Default value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. + Nodes that have a label with this key and identical + values are considered to be in the same topology. + We consider each as a "bucket", and + try to put balanced number of pods into each bucket. + It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal + with a pod if it doesn''t satisfy the spread constraint. + - DoNotSchedule (default) tells the scheduler not + to schedule it - ScheduleAnyway tells the scheduler + to still schedule it It''s considered as "Unsatisfiable" + if and only if placing incoming pod on any topology + violates "MaxSkew". For example, in a 3-zone cluster, + MaxSkew is set to 1, and pods with the same labelSelector + spread as 3/1/1: | zone1 | zone2 | zone3 | | P P + P | P | P | If WhenUnsatisfiable is set + to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) + on zone2(zone3) satisfies MaxSkew(1). In other words, + the cluster can still be imbalanced, but scheduler + won''t make it *more* imbalanced. It''s a required + field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + description: 'List of volumes that can be mounted by containers + belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' + items: + description: Volume represents a named volume in a pod + that may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS + Disk resource that is attached to a kubelet''s host + machine and then exposed to the pod. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that + you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume + /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda + is "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set + the ReadOnly property in VolumeMounts to "true". + If omitted, the default is "false". More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk + mount on the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, + Read Write.' + type: string + diskName: + description: The Name of the data disk in the + blob storage + type: string + diskURI: + description: The URI the data disk in the blob + storage + type: string + fsType: + description: Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple + blob disks per storage account Dedicated: single + blob disk per storage account Managed: azure + managed data disk (only in managed availability + set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service + mount on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on + the host that shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path + to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference + to the authentication secret for User, default + is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user + name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached + and mounted on kubelets host machine. More info: + https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object + containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume + in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that + should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a value between 0 + and 0777. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that + affect the file mode, like fsGroup, and the + result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the ConfigMap, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element + '..'. May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its keys must be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents + storage that is handled by an external CSI driver + (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver + that handles this volume. Consult with your + admin for the correct name as registered in + the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", + "xfs", "ntfs". If not provided, the empty value + is passed to the associated CSI driver which + will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference + to the secret object containing sensitive information + to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if + no secret is required. If the secret object + contains more than one secret, all secret references + are passed. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific + properties that are passed to the CSI driver. + Consult your driver's documentation for supported + values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about + the pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a value between 0 + and 0777. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that + affect the file mode, like fsGroup, and the + result can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should + back this directory. The default is "" which + means to use the node''s default medium. Must + be an empty string (default) or Memory. More + info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage required + for this EmptyDir volume. The size limit is + also applicable for memory medium. The maximum + usage on memory medium EmptyDir would be the + minimum value between the SizeLimit specified + here and the sum of memory limits of all containers + in a pod. The default is nil which means that + the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + fc: + description: FC represents a Fibre Channel resource + that is attached to a kubelet's host machine and + then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. TODO: + how do we prevent errors in the filesystem from + compromising the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names + (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs + and lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume + resource that is provisioned/attached using an exec + based plugin. + properties: + driver: + description: Driver is the name of the driver + to use for this volume. + type: string + fsType: + description: Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". The default + filesystem depends on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options + if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty + if no secret object is specified. If the secret + object contains more than one secret, all secrets + are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached + to a kubelet's host machine. This depends on the + Flocker control service being running + properties: + datasetName: + description: Name of the dataset stored as metadata + -> name on the dataset for Flocker should be + considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique + identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk + resource that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that + you want to mount. If omitted, the default is + to mount by volume name. Examples: For volume + /dev/sda1, you specify the partition as "1". + Similarly, the volume partition for /dev/sda + is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in + GCE. Used to identify the disk in GCE. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository + at a particular revision. DEPRECATED: GitRepo is + deprecated. To provision a container with a git + repo, mount an EmptyDir into an InitContainer that + clones the repo using git, then mount the EmptyDir + into the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain + or start with '..'. If '.' is supplied, the + volume directory will be the git repository. Otherwise, + if specified, the volume will contain the git + repository in the subdirectory with the given + name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount + on the host that shares a pod''s lifetime. More + info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name + that details Glusterfs topology. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs + volume to be mounted with read-only permissions. + Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file + or directory on the host machine that is directly + exposed to the container. This is generally used + for system agents or other privileged things that + are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use + host directory mounts and who can/can not mount + host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. + If the path is a symlink, it will follow the + link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults + to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource + that is attached to a kubelet''s host machine and + then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP + authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP + authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, + new iSCSI interface : will be created for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an + iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal + is either an IP or ip_addr:port if the port + is other than default (typically TCP ports 860 + and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and + initiator authentication + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is + either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 + and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL + and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host + that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS + server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS + export to be mounted with read-only permissions. + Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address + of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents + a reference to a PersistentVolumeClaim in the same + namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this + volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets + host machine + properties: + fsType: + description: Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx + volume attached and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem + type to mount Must be a filesystem type supported + by the host operating system. Ex. "ext4", "xfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx + volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: Mode bits to use on created files + by default. Must be a value between 0 and 0777. + Directories within the path are not affected + by this setting. This might be in conflict with + other options that affect the file mode, like + fsGroup, and the result can be other mode bits + set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected + along with other supported volume types + properties: + configMap: + description: information about the configMap + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + ConfigMap will be projected into the + volume as a file whose name is the + key and content is the value. If specified, + the listed keys will be projected + into the specified paths, and unlisted + keys will not be present. If a key + is specified which is not present + in the ConfigMap, the volume setup + will error unless it is marked optional. + Paths must be relative and may not + contain the '..' path or start with + '..'. + items: + description: Maps a string key to + a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + to use on this file, must be + a value between 0 and 0777. + If not specified, the volume + defaultMode will be used. This + might be in conflict with other + options that affect the file + mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path + of the file to map the key to. + May not be an absolute path. + May not contain the path element + '..'. May not start with the + string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile + represents information to create + the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only annotations, + labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the + schema the FieldPath is + written in terms of, defaults + to "v1". + type: string + fieldPath: + description: Path of the field + to select in the specified + API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits + to use on this file, must be + a value between 0 and 0777. + If not specified, the volume + defaultMode will be used. This + might be in conflict with other + options that affect the file + mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file + to be created. Must not be absolute + or contain the ''..'' path. + Must be utf-8 encoded. The first + item of the relative path must + not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu + and requests.memory) are currently + supported.' + properties: + containerName: + description: 'Container name: + required for volumes, optional + for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the + output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret + data to project + properties: + items: + description: If unspecified, each key-value + pair in the Data field of the referenced + Secret will be projected into the + volume as a file whose name is the + key and content is the value. If specified, + the listed keys will be projected + into the specified paths, and unlisted + keys will not be present. If a key + is specified which is not present + in the Secret, the volume setup will + error unless it is marked optional. + Paths must be relative and may not + contain the '..' path or start with + '..'. + items: + description: Maps a string key to + a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits + to use on this file, must be + a value between 0 and 0777. + If not specified, the volume + defaultMode will be used. This + might be in conflict with other + options that affect the file + mode, like fsGroup, and the + result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path + of the file to map the key to. + May not be an absolute path. + May not contain the path element + '..'. May not start with the + string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended + audience of the token. A recipient + of a token must identify itself with + an identifier specified in the audience + of the token, and otherwise should + reject the token. The audience defaults + to the identifier of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the + requested duration of validity of + the service account token. As the + token approaches expiration, the kubelet + volume plugin will proactively rotate + the service account token. The kubelet + will start trying to rotate the token + if the token is older than 80 percent + of its time to live or if the token + is older than 24 hours.Defaults to + 1 hour and must be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative + to the mount point of the file to + project the token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on + the host that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default + is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte + volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple + Quobyte Registry services specified as a string + as host:port pair (multiple entries are separated + with commas) which acts as the central registry + for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume + in the Backend Used with dynamically provisioned + Quobyte volumes, value is set by the plugin + type: string + user: + description: User to map volume access to Defaults + to serivceaccount user + type: string + volume: + description: Volume is a string that references + an already created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device + mount on the host that shares a pod''s lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + image: + description: 'The rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring + for RBDUser. Default is /etc/ceph/keyring. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is + rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication + secret for RBDUser. If provided overrides keyring. + Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is + admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent + volume attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Default is + "xfs". + type: string + gateway: + description: The host address of the ScaleIO API + Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection + Domain for the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret + for ScaleIO user and other sensitive information. + If this is not provided, Login operation will + fail. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: The name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: The name of a volume already created + in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should + populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created + files by default. Must be a value between 0 + and 0777. Defaults to 0644. Directories within + the path are not affected by this setting. This + might be in conflict with other options that + affect the file mode, like fsGroup, and the + result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose + name is the key and content is the value. If + specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the Secret, the volume setup + will error unless it is marked optional. Paths + must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might be + in conflict with other options that affect + the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element + '..'. May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its + keys must be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s + namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to + use for obtaining the StorageOS API credentials. If + not specified, default values will be attempted. + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable + name of the StorageOS volume. Volume names + are only unique within a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope + of the volume within StorageOS. If no namespace + is specified then the Pod's namespace will be + used. This allows the Kubernetes name scoping + to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override + the default behaviour. Set to "default" if you + are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will + be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume + attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be + a filesystem type supported by the host operating + system. Ex. "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) + profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) + profile name. + type: string + volumePath: + description: Path that identifies vSphere volume + vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + required: + - containers + type: object + type: object + ttlSecondsAfterFinished: + description: ttlSecondsAfterFinished limits the lifetime of a Job + that has finished execution (either Complete or Failed). If this + field is set, ttlSecondsAfterFinished after the Job finishes, + it is eligible to be automatically deleted. When the Job is being + deleted, its lifecycle guarantees (e.g. finalizers) will be honored. + If this field is unset, the Job won't be automatically deleted. + If this field is set to zero, the Job becomes eligible to be deleted + immediately after it finishes. This field is alpha-level and is + only honored by servers that enable the TTLAfterFinished feature. + format: int32 + type: integer + required: + - template + type: object + maxReplicaCount: + format: int32 + type: integer + pollingInterval: + format: int32 + type: integer + successfulJobsHistoryLimit: + format: int32 + type: integer + triggers: + items: + description: ScaleTriggers reference the scaler that will be used + properties: + authenticationRef: + description: ScaledObjectAuthRef points to the TriggerAuthentication + object that is used to authenticate the scaler with the environment + properties: + name: + type: string + required: + - name + type: object + metadata: + additionalProperties: + type: string + type: object + name: + type: string + type: + type: string + required: + - metadata + - type + type: object + type: array + required: + - jobTargetRef + - triggers + type: object + status: + description: ScaledJobStatus defines the observed state of ScaledJob + properties: + conditions: + description: Conditions an array representation to store multiple Conditions + items: + description: Condition to store the condition state + properties: + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition + type: string + required: + - status + - type + type: object + type: array + lastActiveTime: + format: date-time + type: string + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + labels: + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: scaledobjects.keda.sh +spec: + additionalPrinterColumns: + - JSONPath: .status.scaleTargetKind + name: ScaleTargetKind + type: string + - JSONPath: .spec.scaleTargetRef.name + name: ScaleTargetName + type: string + - JSONPath: .spec.triggers[*].type + name: Triggers + type: string + - JSONPath: .spec.triggers[*].authenticationRef.name + name: Authentication + type: string + - JSONPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - JSONPath: .status.conditions[?(@.type=="Active")].status + name: Active + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: keda.sh + names: + kind: ScaledObject + listKind: ScaledObjectList + plural: scaledobjects + shortNames: + - so + singular: scaledobject + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: ScaledObject is a specification for a ScaledObject resource + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ScaledObjectSpec is the spec for a ScaledObject resource + properties: + advanced: + description: AdvancedConfig specifies advance scaling options + properties: + horizontalPodAutoscalerConfig: + description: HorizontalPodAutoscalerConfig specifies horizontal + scale config + properties: + behavior: + description: HorizontalPodAutoscalerBehavior configures the + scaling behavior of the target in both Up and Down directions + (scaleUp and scaleDown fields respectively). + properties: + scaleDown: + description: scaleDown is scaling policy for scaling Down. + If not set, the default value is to allow to scale down + to minReplicas pods, with a 300 second stabilization window + (i.e., the highest recommendation for the last 300sec + is used). + properties: + policies: + description: policies is a list of potential scaling + polices which can be used during scaling. At least + one policy must be specified, otherwise the HPAScalingRules + will be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: PeriodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and + less than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: Type is used to specify the scaling + policy. + type: string + value: + description: Value contains the amount of change + which is permitted by the policy. It must be + greater than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value MaxPolicySelect + is used. + type: string + stabilizationWindowSeconds: + description: 'StabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. StabilizationWindowSeconds + must be greater than or equal to zero and less than + or equal to 3600 (one hour). If not set, use the default + values: - For scale up: 0 (i.e. no stabilization is + done). - For scale down: 300 (i.e. the stabilization + window is 300 seconds long).' + format: int32 + type: integer + type: object + scaleUp: + description: 'scaleUp is scaling policy for scaling Up. + If not set, the default value is the higher of: * increase + no more than 4 pods per 60 seconds * double the number + of pods per 60 seconds No stabilization is used.' + properties: + policies: + description: policies is a list of potential scaling + polices which can be used during scaling. At least + one policy must be specified, otherwise the HPAScalingRules + will be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: PeriodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and + less than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: Type is used to specify the scaling + policy. + type: string + value: + description: Value contains the amount of change + which is permitted by the policy. It must be + greater than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value MaxPolicySelect + is used. + type: string + stabilizationWindowSeconds: + description: 'StabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. StabilizationWindowSeconds + must be greater than or equal to zero and less than + or equal to 3600 (one hour). If not set, use the default + values: - For scale up: 0 (i.e. no stabilization is + done). - For scale down: 300 (i.e. the stabilization + window is 300 seconds long).' + format: int32 + type: integer + type: object + type: object + resourceMetrics: + items: + description: ResourceMetricSource indicates how to scale on + a resource metric known to Kubernetes, as specified in requests + and limits, describing each pod in the current scale target + (e.g. CPU or memory). The values will be averaged together + before being compared to the target. Such metrics are built + in to Kubernetes, and have special scaling options on top + of those available to normal per-pod metrics using the "pods" + source. Only one "target" type should be set. + properties: + name: + description: name is the name of the resource in question. + type: string + target: + description: target specifies the target value for the + given metric + properties: + averageUtilization: + description: averageUtilization is the target value + of the average of the resource metric across all + relevant pods, represented as a percentage of the + requested value of the resource for the pods. Currently + only valid for Resource metric source type + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target value of the + average of the metric across all relevant pods (as + a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the metric type + is Utilization, Value, or AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of the metric + (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - name + - target + type: object + type: array + type: object + restoreToOriginalReplicaCount: + type: boolean + type: object + cooldownPeriod: + format: int32 + type: integer + maxReplicaCount: + format: int32 + type: integer + minReplicaCount: + format: int32 + type: integer + pollingInterval: + format: int32 + type: integer + scaleTargetRef: + description: ScaleTarget holds the a reference to the scale target Object + properties: + apiVersion: + type: string + envSourceContainerName: + type: string + kind: + type: string + name: + type: string + required: + - name + type: object + triggers: + items: + description: ScaleTriggers reference the scaler that will be used + properties: + authenticationRef: + description: ScaledObjectAuthRef points to the TriggerAuthentication + object that is used to authenticate the scaler with the environment + properties: + name: + type: string + required: + - name + type: object + metadata: + additionalProperties: + type: string + type: object + name: + type: string + type: + type: string + required: + - metadata + - type + type: object + type: array + required: + - scaleTargetRef + - triggers + type: object + status: + description: ScaledObjectStatus is the status for a ScaledObject resource + properties: + conditions: + description: Conditions an array representation to store multiple Conditions + items: + description: Condition to store the condition state + properties: + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition + type: string + required: + - status + - type + type: object + type: array + externalMetricNames: + items: + type: string + type: array + lastActiveTime: + format: date-time + type: string + originalReplicaCount: + format: int32 + type: integer + scaleTargetGVKR: + description: GroupVersionKindResource provides unified structure for + schema.GroupVersionKind and Resource + properties: + group: + type: string + kind: + type: string + resource: + type: string + version: + type: string + required: + - group + - kind + - resource + - version + type: object + scaleTargetKind: + type: string + type: object + required: + - spec + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + labels: + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: triggerauthentications.keda.sh +spec: + additionalPrinterColumns: + - JSONPath: .spec.podIdentity.provider + name: PodIdentity + type: string + - JSONPath: .spec.secretTargetRef[*].name + name: Secret + type: string + - JSONPath: .spec.env[*].name + name: Env + type: string + group: keda.sh + names: + kind: TriggerAuthentication + listKind: TriggerAuthenticationList + plural: triggerauthentications + shortNames: + - ta + - triggerauth + singular: triggerauthentication + scope: Namespaced + subresources: {} + validation: + openAPIV3Schema: + description: TriggerAuthentication defines how a trigger can authenticate + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TriggerAuthenticationSpec defines the various ways to authenticate + properties: + env: + items: + description: AuthEnvironment is used to authenticate using environment + variables in the destination ScaleTarget spec + properties: + containerName: + type: string + name: + type: string + parameter: + type: string + required: + - name + - parameter + type: object + type: array + hashiCorpVault: + description: HashiCorpVault is used to authenticate using Hashicorp + Vault + properties: + address: + type: string + authentication: + description: VaultAuthentication contains the list of Hashicorp + Vault authentication methods + type: string + credential: + description: Credential defines the Hashicorp Vault credentials + depending on the authentication method + properties: + serviceAccount: + type: string + token: + type: string + type: object + mount: + type: string + role: + type: string + secrets: + items: + description: VaultSecret defines the mapping between the path + of the secret in Vault to the parameter + properties: + key: + type: string + parameter: + type: string + path: + type: string + required: + - key + - parameter + - path + type: object + type: array + required: + - address + - authentication + - secrets + type: object + podIdentity: + description: AuthPodIdentity allows users to select the platform native + identity mechanism + properties: + provider: + description: PodIdentityProvider contains the list of providers + type: string + required: + - provider + type: object + secretTargetRef: + items: + description: AuthSecretTargetRef is used to authenticate using a reference + to a secret + properties: + key: + type: string + name: + type: string + parameter: + type: string + required: + - key + - name + - parameter + type: object + type: array + type: object + required: + - spec + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: keda-operator + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-operator + namespace: keda +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: keda-external-metrics-reader + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-external-metrics-reader +rules: +- apiGroups: + - external.metrics.k8s.io + resources: + - '*' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/name: keda-operator + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-operator +rules: +- apiGroups: + - "" + resources: + - configmaps + - configmaps/status + - events + verbs: + - '*' +- apiGroups: + - "" + resources: + - external + - pods + - secrets + - services + verbs: + - get + - list + - watch +- apiGroups: + - '*' + resources: + - '*' + verbs: + - get +- apiGroups: + - '*' + resources: + - '*/scale' + verbs: + - '*' +- apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - '*' +- apiGroups: + - batch + resources: + - jobs + verbs: + - '*' +- apiGroups: + - keda.sh + resources: + - scaledjobs + - scaledjobs/status + verbs: + - '*' +- apiGroups: + - keda.sh + resources: + - scaledobjects + - scaledobjects/finalizers + - scaledobjects/status + verbs: + - '*' +- apiGroups: + - keda.sh + resources: + - triggerauthentications + - triggerauthentications/status + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: keda-auth-reader + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-auth-reader + namespace: keda +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: extension-apiserver-authentication-reader +subjects: +- kind: ServiceAccount + name: keda-operator + namespace: keda +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: keda-hpa-controller-external-metrics + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-hpa-controller-external-metrics +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: keda-external-metrics-reader +subjects: +- kind: ServiceAccount + name: horizontal-pod-autoscaler + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: keda-operator + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: keda-operator +subjects: +- kind: ServiceAccount + name: keda-operator + namespace: keda +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: keda-system-auth-delegator + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda:system:auth-delegator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:auth-delegator +subjects: +- kind: ServiceAccount + name: keda-operator + namespace: keda +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: keda-metrics-apiserver + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-metrics-apiserver + namespace: keda +spec: + ports: + - name: https + port: 443 + targetPort: 6443 + - name: http + port: 80 + targetPort: 8080 + selector: + app: keda-metrics-apiserver + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: keda-metrics-apiserver + app.kubernetes.io/name: keda-metrics-apiserver + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-metrics-apiserver + namespace: keda +spec: + replicas: 1 + selector: + matchLabels: + app: keda-metrics-apiserver + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + template: + metadata: + labels: + app: keda-metrics-apiserver + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-metrics-apiserver + spec: + containers: + - args: + - /usr/local/bin/keda-adapter + - --secure-port=6443 + - --logtostderr=true + - --v=4 + env: + - name: WATCH_NAMESPACE + value: "" + image: docker.io/kedacore/keda-metrics-apiserver:2.0.0-beta + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 6443 + scheme: HTTPS + initialDelaySeconds: 5 + name: keda-metrics-apiserver + ports: + - containerPort: 6443 + name: https + - containerPort: 8080 + name: http + readinessProbe: + httpGet: + path: /readyz + port: 6443 + scheme: HTTPS + initialDelaySeconds: 5 + resources: + limits: + cpu: 1000m + memory: 1000Mi + requests: + cpu: 100m + memory: 100Mi + volumeMounts: + - mountPath: /tmp + name: temp-vol + nodeSelector: + beta.kubernetes.io/os: linux + serviceAccountName: keda-operator + volumes: + - emptyDir: {} + name: temp-vol +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: keda-operator + app.kubernetes.io/component: operator + app.kubernetes.io/name: keda-operator + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-operator + namespace: keda +spec: + replicas: 1 + selector: + matchLabels: + app: keda-operator + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + template: + metadata: + labels: + app: keda-operator + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: keda-operator + name: keda-operator + spec: + containers: + - args: + - --enable-leader-election + - --zap-log-level=debug + - --zap-encoder=console + command: + - /keda + env: + - name: WATCH_NAMESPACE + value: "" + image: docker.io/kedacore/keda:2.0.0-beta + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 25 + name: keda-operator + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 20 + resources: + limits: + cpu: 1000m + memory: 1000Mi + requests: + cpu: 100m + memory: 100Mi + nodeSelector: + beta.kubernetes.io/os: linux + serviceAccountName: keda-operator + terminationGracePeriodSeconds: 10 +--- +apiVersion: apiregistration.k8s.io/v1 +kind: APIService +metadata: + labels: + app.kubernetes.io/name: v1beta1.external.metrics.k8s.io + app.kubernetes.io/part-of: keda-operator + app.kubernetes.io/version: 2.0.0-beta + name: v1beta1.external.metrics.k8s.io +spec: + group: external.metrics.k8s.io + groupPriorityMinimum: 100 + insecureSkipTLSVerify: true + service: + name: keda-metrics-apiserver + namespace: keda + version: v1beta1 + versionPriority: 100 diff --git a/autoscaling-on-queue/deployment/producer.yaml b/autoscaling-on-queue/deployment/producer.yaml index 649794f..2df86c2 100644 --- a/autoscaling-on-queue/deployment/producer.yaml +++ b/autoscaling-on-queue/deployment/producer.yaml @@ -1,40 +1,40 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: autoscaling-producer - labels: - app: autoscaling-producer -spec: - selector: - matchLabels: - app: autoscaling-producer - template: - metadata: - labels: - app: autoscaling-producer - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "autoscaling-producer" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60034" - dapr.io/log-level: "debug" - dapr.io/log-as-json: "true" - spec: - containers: - - name: service - image: mchmarny/autoscaling-producer:v0.11.1 - ports: - - containerPort: 60034 - env: - - name: PUBSUB_NAME - value: autoscaling-pubsub - - name: TOPIC_NAME - value: metric - - name: NUMBER_OF_PUBLISHERS - value: "2" - - name: PUBLISHERS_FREQ - value: "100ms" - - name: PUBLISHERS_DELAY - value: "10s" - - name: LOG_FREQ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: autoscaling-producer + labels: + app: autoscaling-producer +spec: + selector: + matchLabels: + app: autoscaling-producer + template: + metadata: + labels: + app: autoscaling-producer + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "autoscaling-producer" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60034" + dapr.io/log-level: "debug" + dapr.io/log-as-json: "true" + spec: + containers: + - name: service + image: mchmarny/autoscaling-producer:v0.11.1 + ports: + - containerPort: 60034 + env: + - name: PUBSUB_NAME + value: autoscaling-pubsub + - name: TOPIC_NAME + value: metric + - name: NUMBER_OF_PUBLISHERS + value: "2" + - name: PUBLISHERS_FREQ + value: "100ms" + - name: PUBLISHERS_DELAY + value: "10s" + - name: LOG_FREQ value: "3s" \ No newline at end of file diff --git a/autoscaling-on-queue/deployment/subscriber-scaler.yaml b/autoscaling-on-queue/deployment/subscriber-scaler.yaml index fa50de2..bd23cc3 100644 --- a/autoscaling-on-queue/deployment/subscriber-scaler.yaml +++ b/autoscaling-on-queue/deployment/subscriber-scaler.yaml @@ -1,18 +1,18 @@ -apiVersion: keda.sh/v1alpha1 -kind: ScaledObject -metadata: - name: subscriber-scaler -spec: - scaleTargetRef: - name: autoscaling-subscriber - pollingInterval: 15 - minReplicaCount: 0 - maxReplicaCount: 10 - cooldownPeriod: 30 - triggers: - - type: kafka - metadata: - topic: metric - bootstrapServers: kafka-cp-kafka.kafka.svc.cluster.local:9092 - consumerGroup: autoscaling-subscriber +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: subscriber-scaler +spec: + scaleTargetRef: + name: autoscaling-subscriber + pollingInterval: 15 + minReplicaCount: 0 + maxReplicaCount: 10 + cooldownPeriod: 30 + triggers: + - type: kafka + metadata: + topic: metric + bootstrapServers: kafka-cp-kafka.kafka.svc.cluster.local:9092 + consumerGroup: autoscaling-subscriber lagThreshold: "3" \ No newline at end of file diff --git a/autoscaling-on-queue/deployment/subscriber.yaml b/autoscaling-on-queue/deployment/subscriber.yaml index 7bce5ea..dc68d31 100644 --- a/autoscaling-on-queue/deployment/subscriber.yaml +++ b/autoscaling-on-queue/deployment/subscriber.yaml @@ -1,34 +1,34 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: autoscaling-subscriber - labels: - app: autoscaling-subscriber -spec: - selector: - matchLabels: - app: autoscaling-subscriber - template: - metadata: - labels: - app: autoscaling-subscriber - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "autoscaling-subscriber" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60033" - dapr.io/log-level: "debug" - dapr.io/log-as-json: "true" - spec: - containers: - - name: service - image: mchmarny/autoscaling-subscriber:v0.11.1 - ports: - - containerPort: 60033 - env: - - name: PUBSUB_NAME - value: autoscaling-pubsub - - name: TOPIC_NAME - value: metric - - name: PROCESS_DURATION +apiVersion: apps/v1 +kind: Deployment +metadata: + name: autoscaling-subscriber + labels: + app: autoscaling-subscriber +spec: + selector: + matchLabels: + app: autoscaling-subscriber + template: + metadata: + labels: + app: autoscaling-subscriber + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "autoscaling-subscriber" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60033" + dapr.io/log-level: "debug" + dapr.io/log-as-json: "true" + spec: + containers: + - name: service + image: mchmarny/autoscaling-subscriber:v0.11.1 + ports: + - containerPort: 60033 + env: + - name: PUBSUB_NAME + value: autoscaling-pubsub + - name: TOPIC_NAME + value: metric + - name: PROCESS_DURATION value: "300ms" \ No newline at end of file diff --git a/autoscaling-on-queue/producer/Dockerfile b/autoscaling-on-queue/producer/Dockerfile index f64cfb9..2c9aa9e 100644 --- a/autoscaling-on-queue/producer/Dockerfile +++ b/autoscaling-on-queue/producer/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./producer . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/producer . - -ENTRYPOINT ["./producer"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./producer . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/producer . + +ENTRYPOINT ["./producer"] diff --git a/autoscaling-on-queue/producer/Makefile b/autoscaling-on-queue/producer/Makefile index 6995d04..3441902 100644 --- a/autoscaling-on-queue/producer/Makefile +++ b/autoscaling-on-queue/producer/Makefile @@ -1,40 +1,40 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=autoscaling-producer -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: run -run: tidy ## Runs uncompiled code - NUMBER_OF_PUBLISHERS=3 PUBLISH_TO_CONSOLE=true go run main.go - -.PHONY: image -image: tidy ## Builds and publishes docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=autoscaling-producer +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: run +run: tidy ## Runs uncompiled code + NUMBER_OF_PUBLISHERS=3 PUBLISH_TO_CONSOLE=true go run main.go + +.PHONY: image +image: tidy ## Builds and publishes docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/autoscaling-on-queue/producer/go.mod b/autoscaling-on-queue/producer/go.mod index f6a4e14..2e601e3 100644 --- a/autoscaling-on-queue/producer/go.mod +++ b/autoscaling-on-queue/producer/go.mod @@ -1,11 +1,11 @@ -module github.com/mchmarny/dapr-demos/autoscaling-on-queue/producer - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/google/uuid v1.1.2 - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect - google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect -) +module github.com/mchmarny/dapr-demos/autoscaling-on-queue/producer + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/google/uuid v1.1.2 + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect + google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect +) diff --git a/autoscaling-on-queue/producer/go.sum b/autoscaling-on-queue/producer/go.sum index 6104df0..911c850 100644 --- a/autoscaling-on-queue/producer/go.sum +++ b/autoscaling-on-queue/producer/go.sum @@ -1,105 +1,105 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/autoscaling-on-queue/producer/main.go b/autoscaling-on-queue/producer/main.go index 0f78a3a..23b670c 100644 --- a/autoscaling-on-queue/producer/main.go +++ b/autoscaling-on-queue/producer/main.go @@ -1,207 +1,207 @@ -package main - -import ( - "context" - "crypto/sha256" - "encoding/json" - "fmt" - "log" - "math/rand" - "os" - "os/signal" - "strconv" - "strings" - "sync" - "syscall" - "time" - - dapr "github.com/dapr/go-sdk/client" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/google/uuid" -) - -const ( - chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -) - -var ( - logger = log.New(os.Stdout, "", 0) - serviceAddress = getEnvVar("ADDRESS", ":60034") - pubSubName = getEnvVar("PUBSUB_NAME", "autoscaling-pubsub") - topicName = getEnvVar("TOPIC_NAME", "metrics") - numOfPublishers = getEnvIntOrFail("NUMBER_OF_PUBLISHERS", "1") - publishFrequency = getEnvDurationOrFail("PUBLISHERS_FREQ", "1s") - publishDelay = getEnvDurationOrFail("PUBLISHERS_DELAY", "10s") - logFrequency = getEnvDurationOrFail("LOG_FREQ", "3s") - publishToConsole = getEnvBoolOrFail("PUBLISH_TO_CONSOLE", "false") - - client dapr.Client -) - -func main() { - if numOfPublishers < 1 { - numOfPublishers = 1 - } - logger.Printf("subscription name: %s", pubSubName) - logger.Printf("topic name: %s", topicName) - logger.Printf("number of publishers: %d", numOfPublishers) - logger.Printf("publish frequency: %v", publishFrequency) - logger.Printf("log frequency: %v", logFrequency) - logger.Printf("publish delay: %v", publishDelay) - - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - c, err := dapr.NewClient() - if err != nil { - log.Fatalf("error creating Dapr client: %v", err) - } - client = c - defer client.Close() - - // handle signals - stop := make(chan os.Signal) - signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT) - - resultCh := make(chan bool, 100) - stopCh := make(chan struct{}) - - go func() { - <-stop - close(stopCh) - }() - - // print results - go monitor(resultCh, stopCh) - - // start producing - for i := 1; i <= numOfPublishers; i++ { - go publish(i, resultCh, stopCh) - } - - // start the server to handle incoming events - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } - -} - -func monitor(resultCh <-chan bool, stopCh <-chan struct{}) { - var mux sync.Mutex - var successCounter int64 = 0 - var errorCounter int64 = 0 - startTime := time.Now() - tickerCh := time.NewTicker(logFrequency).C - for { - select { - case r := <-resultCh: - mux.Lock() - if r { - successCounter++ - } else { - errorCounter++ - } - mux.Unlock() - case <-tickerCh: - var avg float64 = 0 - if successCounter > 0 { - avg = float64(successCounter) / time.Since(startTime).Seconds() - } - logger.Printf("%10d published, %3.0f/sec, %3d errors", successCounter, avg, errorCounter) - case <-stopCh: - os.Exit(0) - } - } -} - -func publish(index int, resultCh chan<- bool, stopCh <-chan struct{}) { - delayCh := time.NewTicker(publishDelay).C - <-delayCh - - tickerCh := time.NewTicker(publishFrequency).C - for { - select { - case <-stopCh: - return - case <-tickerCh: - d := getEventData(index) - if publishToConsole { - logger.Printf("%s", d) - resultCh <- true - continue - } - resultCh <- client.PublishEvent(context.Background(), pubSubName, topicName, d) == nil - } - } -} - -func getEventData(index int) []byte { - r := requestContent{ - ID: fmt.Sprintf("p%d-%s", index, uuid.New().String()), - Data: []byte(getData(256)), - Time: time.Now().UTC().Unix(), - } - - // hash the entire message - inSha := sha256.Sum256(r.Data) - r.Sha = string(inSha[:]) - - b, err := json.Marshal(r) - if err != nil { - logger.Fatalf("error generating request: %v", err) - } - return b -} - -func getData(length int) string { - seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) - b := make([]byte, length) - for i := range b { - b[i] = chars[seededRand.Intn(len(chars))] - } - return string(b) -} - -type requestContent struct { - ID string `json:"id"` - Data []byte `json:"data"` - Sha string `json:"sha"` - Time int64 `json:"time"` -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} - -func getEnvIntOrFail(key, fallbackValue string) int { - s := getEnvVar(key, fallbackValue) - v, err := strconv.Atoi(s) - if err != nil { - logger.Fatalf("invalid number variable: %s - %v", s, err) - } - return v -} - -func getEnvDurationOrFail(key, fallbackValue string) time.Duration { - s := getEnvVar(key, fallbackValue) - v, err := time.ParseDuration(s) - if err != nil { - logger.Fatalf("invalid duration variable: %s - %v", s, err) - } - return v -} - -func getEnvBoolOrFail(key, fallbackValue string) bool { - s := getEnvVar(key, fallbackValue) - v, err := strconv.ParseBool(s) - if err != nil { - logger.Fatalf("invalid bool variable: %s - %v", s, err) - } - return v -} +package main + +import ( + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "log" + "math/rand" + "os" + "os/signal" + "strconv" + "strings" + "sync" + "syscall" + "time" + + dapr "github.com/dapr/go-sdk/client" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/google/uuid" +) + +const ( + chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +) + +var ( + logger = log.New(os.Stdout, "", 0) + serviceAddress = getEnvVar("ADDRESS", ":60034") + pubSubName = getEnvVar("PUBSUB_NAME", "autoscaling-pubsub") + topicName = getEnvVar("TOPIC_NAME", "metrics") + numOfPublishers = getEnvIntOrFail("NUMBER_OF_PUBLISHERS", "1") + publishFrequency = getEnvDurationOrFail("PUBLISHERS_FREQ", "1s") + publishDelay = getEnvDurationOrFail("PUBLISHERS_DELAY", "10s") + logFrequency = getEnvDurationOrFail("LOG_FREQ", "3s") + publishToConsole = getEnvBoolOrFail("PUBLISH_TO_CONSOLE", "false") + + client dapr.Client +) + +func main() { + if numOfPublishers < 1 { + numOfPublishers = 1 + } + logger.Printf("subscription name: %s", pubSubName) + logger.Printf("topic name: %s", topicName) + logger.Printf("number of publishers: %d", numOfPublishers) + logger.Printf("publish frequency: %v", publishFrequency) + logger.Printf("log frequency: %v", logFrequency) + logger.Printf("publish delay: %v", publishDelay) + + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + c, err := dapr.NewClient() + if err != nil { + log.Fatalf("error creating Dapr client: %v", err) + } + client = c + defer client.Close() + + // handle signals + stop := make(chan os.Signal) + signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT) + + resultCh := make(chan bool, 100) + stopCh := make(chan struct{}) + + go func() { + <-stop + close(stopCh) + }() + + // print results + go monitor(resultCh, stopCh) + + // start producing + for i := 1; i <= numOfPublishers; i++ { + go publish(i, resultCh, stopCh) + } + + // start the server to handle incoming events + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } + +} + +func monitor(resultCh <-chan bool, stopCh <-chan struct{}) { + var mux sync.Mutex + var successCounter int64 = 0 + var errorCounter int64 = 0 + startTime := time.Now() + tickerCh := time.NewTicker(logFrequency).C + for { + select { + case r := <-resultCh: + mux.Lock() + if r { + successCounter++ + } else { + errorCounter++ + } + mux.Unlock() + case <-tickerCh: + var avg float64 = 0 + if successCounter > 0 { + avg = float64(successCounter) / time.Since(startTime).Seconds() + } + logger.Printf("%10d published, %3.0f/sec, %3d errors", successCounter, avg, errorCounter) + case <-stopCh: + os.Exit(0) + } + } +} + +func publish(index int, resultCh chan<- bool, stopCh <-chan struct{}) { + delayCh := time.NewTicker(publishDelay).C + <-delayCh + + tickerCh := time.NewTicker(publishFrequency).C + for { + select { + case <-stopCh: + return + case <-tickerCh: + d := getEventData(index) + if publishToConsole { + logger.Printf("%s", d) + resultCh <- true + continue + } + resultCh <- client.PublishEvent(context.Background(), pubSubName, topicName, d) == nil + } + } +} + +func getEventData(index int) []byte { + r := requestContent{ + ID: fmt.Sprintf("p%d-%s", index, uuid.New().String()), + Data: []byte(getData(256)), + Time: time.Now().UTC().Unix(), + } + + // hash the entire message + inSha := sha256.Sum256(r.Data) + r.Sha = string(inSha[:]) + + b, err := json.Marshal(r) + if err != nil { + logger.Fatalf("error generating request: %v", err) + } + return b +} + +func getData(length int) string { + seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) + b := make([]byte, length) + for i := range b { + b[i] = chars[seededRand.Intn(len(chars))] + } + return string(b) +} + +type requestContent struct { + ID string `json:"id"` + Data []byte `json:"data"` + Sha string `json:"sha"` + Time int64 `json:"time"` +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} + +func getEnvIntOrFail(key, fallbackValue string) int { + s := getEnvVar(key, fallbackValue) + v, err := strconv.Atoi(s) + if err != nil { + logger.Fatalf("invalid number variable: %s - %v", s, err) + } + return v +} + +func getEnvDurationOrFail(key, fallbackValue string) time.Duration { + s := getEnvVar(key, fallbackValue) + v, err := time.ParseDuration(s) + if err != nil { + logger.Fatalf("invalid duration variable: %s - %v", s, err) + } + return v +} + +func getEnvBoolOrFail(key, fallbackValue string) bool { + s := getEnvVar(key, fallbackValue) + v, err := strconv.ParseBool(s) + if err != nil { + logger.Fatalf("invalid bool variable: %s - %v", s, err) + } + return v +} diff --git a/autoscaling-on-queue/subscriber/Dockerfile b/autoscaling-on-queue/subscriber/Dockerfile index f5904ac..a5c01c7 100644 --- a/autoscaling-on-queue/subscriber/Dockerfile +++ b/autoscaling-on-queue/subscriber/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/autoscaling-on-queue/subscriber/Makefile b/autoscaling-on-queue/subscriber/Makefile index 3d996a5..72fd01b 100644 --- a/autoscaling-on-queue/subscriber/Makefile +++ b/autoscaling-on-queue/subscriber/Makefile @@ -1,71 +1,71 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=autoscaling-subscriber -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60022 \ - --app-protocol grpc \ - --components-path ./config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publishes docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/binding.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/$(SERVICE_NAME) - kubectl rollout status deployment/$(SERVICE_NAME) - -.PHONY: autoscale -autoscale: ## Applies Keda to the deployed service - kubectl apply -f k8s/keda.yaml - -.PHONY: scale -scale: ## Scales the subscriber manually - # kubectl autoscale deployment autoscaling-subscriber --min=1 --max=10 - kubectl scale deployment/$(SERVICE_NAME) --replicas=10 - -.PHONY: kafka-port -kafka-port: ## Forwards cluster Kafka port locally - kubectl port-forward svc/kafka -n kafka 9092 & - @echo use 'pkill kubectl -9' to stop - -.PHONY: kafka-portless -kafka-portless: ## Stops forwarding cluster Kafka port locally - @echo use 'pkill kubectl -9' to stop - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=autoscaling-subscriber +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60022 \ + --app-protocol grpc \ + --components-path ./config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publishes docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/binding.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/$(SERVICE_NAME) + kubectl rollout status deployment/$(SERVICE_NAME) + +.PHONY: autoscale +autoscale: ## Applies Keda to the deployed service + kubectl apply -f k8s/keda.yaml + +.PHONY: scale +scale: ## Scales the subscriber manually + # kubectl autoscale deployment autoscaling-subscriber --min=1 --max=10 + kubectl scale deployment/$(SERVICE_NAME) --replicas=10 + +.PHONY: kafka-port +kafka-port: ## Forwards cluster Kafka port locally + kubectl port-forward svc/kafka -n kafka 9092 & + @echo use 'pkill kubectl -9' to stop + +.PHONY: kafka-portless +kafka-portless: ## Stops forwarding cluster Kafka port locally + @echo use 'pkill kubectl -9' to stop + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/autoscaling-on-queue/subscriber/go.mod b/autoscaling-on-queue/subscriber/go.mod index 5182dfe..922622d 100644 --- a/autoscaling-on-queue/subscriber/go.mod +++ b/autoscaling-on-queue/subscriber/go.mod @@ -1,11 +1,11 @@ -module github.com/mchmarny/dapr-demos/autoscaling-on-queue/subscriber - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect - google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect -) +module github.com/mchmarny/dapr-demos/autoscaling-on-queue/subscriber + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect + google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect +) diff --git a/autoscaling-on-queue/subscriber/go.sum b/autoscaling-on-queue/subscriber/go.sum index adb2906..96fb780 100644 --- a/autoscaling-on-queue/subscriber/go.sum +++ b/autoscaling-on-queue/subscriber/go.sum @@ -1,106 +1,106 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/autoscaling-on-queue/subscriber/main.go b/autoscaling-on-queue/subscriber/main.go index 141d89a..24df83e 100644 --- a/autoscaling-on-queue/subscriber/main.go +++ b/autoscaling-on-queue/subscriber/main.go @@ -1,122 +1,122 @@ -package main - -import ( - "context" - "log" - "os/signal" - "sync" - "syscall" - "time" - - "net/http" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/pkg/errors" -) - -const ( - primeStateKey = "high-prime" -) - -var ( - logger = log.New(os.Stdout, "", 0) - reqProcDur time.Duration - - address = getEnvVar("ADDRESS", ":60033") - processDuration = getEnvVar("PROCESS_DURATION", "500ms") - pubSubName = getEnvVar("PUBSUB_NAME", "autoscaling-pubsub") - topicName = getEnvVar("TOPIC_NAME", "metrics") -) - -func main() { - // Dapr service - s, err := daprd.NewService(address) - if err != nil { - logger.Fatalf("failed to start the service: %v", err) - } - - d, err := time.ParseDuration(processDuration) - if err != nil { - logger.Fatalf("invalid parameter (PROCESS_DURATION) must be a duration): %s - %v", processDuration, err) - } - reqProcDur = d - - var mux sync.Mutex - var successCount int64 = 1 - var errorCount int64 = 0 - - resultCh := make(chan bool) - startTime := time.Now() - - go func() { - tickerCh := time.NewTicker(5 * time.Second).C - for { - select { - case r := <-resultCh: - mux.Lock() - if r { - successCount++ - } else { - errorCount++ - } - mux.Unlock() - case <-tickerCh: - var avg float64 = 0 - if successCount > 0 { - avg = float64(successCount) / time.Since(startTime).Seconds() - } - logger.Printf("received: %10d, %3d errors - avg %3.0f/sec", successCount, errorCount, avg) - } - } - }() - - // define subscription - subscription := &common.Subscription{ - PubsubName: pubSubName, - Topic: topicName, - } - - // subscribe - if err := s.AddTopicEventHandler(subscription, func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - if err := processRequest(ctx, e.Data); err != nil { - logger.Printf("error processing request: %v", err) - resultCh <- false - return true, errors.Wrap(err, "error processing request") - } - resultCh <- true - return false, nil - }); err != nil { - logger.Fatalf("error adding topic subscription: %v", err) - } - - // handle signals - done := make(chan os.Signal, 1) - signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - - // Start - go func() { - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("error starting service: %v", err) - } - }() - - // Finish - <-done -} - -// does some computing to keep the process busy organically -func processRequest(ctx context.Context, in interface{}) error { - tickerCh := time.NewTicker(reqProcDur).C - <-tickerCh - return nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + "os/signal" + "sync" + "syscall" + "time" + + "net/http" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/pkg/errors" +) + +const ( + primeStateKey = "high-prime" +) + +var ( + logger = log.New(os.Stdout, "", 0) + reqProcDur time.Duration + + address = getEnvVar("ADDRESS", ":60033") + processDuration = getEnvVar("PROCESS_DURATION", "500ms") + pubSubName = getEnvVar("PUBSUB_NAME", "autoscaling-pubsub") + topicName = getEnvVar("TOPIC_NAME", "metrics") +) + +func main() { + // Dapr service + s, err := daprd.NewService(address) + if err != nil { + logger.Fatalf("failed to start the service: %v", err) + } + + d, err := time.ParseDuration(processDuration) + if err != nil { + logger.Fatalf("invalid parameter (PROCESS_DURATION) must be a duration): %s - %v", processDuration, err) + } + reqProcDur = d + + var mux sync.Mutex + var successCount int64 = 1 + var errorCount int64 = 0 + + resultCh := make(chan bool) + startTime := time.Now() + + go func() { + tickerCh := time.NewTicker(5 * time.Second).C + for { + select { + case r := <-resultCh: + mux.Lock() + if r { + successCount++ + } else { + errorCount++ + } + mux.Unlock() + case <-tickerCh: + var avg float64 = 0 + if successCount > 0 { + avg = float64(successCount) / time.Since(startTime).Seconds() + } + logger.Printf("received: %10d, %3d errors - avg %3.0f/sec", successCount, errorCount, avg) + } + } + }() + + // define subscription + subscription := &common.Subscription{ + PubsubName: pubSubName, + Topic: topicName, + } + + // subscribe + if err := s.AddTopicEventHandler(subscription, func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + if err := processRequest(ctx, e.Data); err != nil { + logger.Printf("error processing request: %v", err) + resultCh <- false + return true, errors.Wrap(err, "error processing request") + } + resultCh <- true + return false, nil + }); err != nil { + logger.Fatalf("error adding topic subscription: %v", err) + } + + // handle signals + done := make(chan os.Signal, 1) + signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + // Start + go func() { + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("error starting service: %v", err) + } + }() + + // Finish + <-done +} + +// does some computing to keep the process busy organically +func processRequest(ctx context.Context, in interface{}) error { + tickerCh := time.NewTicker(reqProcDur).C + <-tickerCh + return nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/component-api/README.md b/component-api/README.md index ec2bf7e..442476f 100644 --- a/component-api/README.md +++ b/component-api/README.md @@ -1,196 +1,196 @@ -# Use of Dapr as a component API Server - -This demo users Dapr instance with API token authentication to show the use of Dapr as a API server for any of its 70+ components. To illustrate, this demo will show two use-cases: - -* Simple note management using Redis state store -* Sending email using Sendgrid output binding -* Querying tweets using Twitter bi-directional binding - -## Setup - -### State Component - -Create a `mongo-secret` - -```shell -kubectl create secret generic redis-secret --from-literal=password="" -``` - -Deploy component and [restart gateway](#ingress-gateway) - -```shell -kubectl apply -f config/state.yaml -``` - -### Email Component - -Create a `email-secret` - -```shell -kubectl create secret generic email-secret --from-literal=apiKey="" -``` - -Deploy component and [restart gateway](#ingress-gateway) - -```shell -kubectl apply -f config/email.yaml -``` - -### Twitter Component - -Create a `twitter-secret` - -```shell -kubectl create secret generic twitter-secret \ - --from-literal=consumerKey="" \ - --from-literal=consumerSecret="" \ - --from-literal=accessToken="" \ - --from-literal=accessSecret="" -``` - -Deploy component and [restart gateway](#ingress-gateway) - -```shell -kubectl apply -f config/twitter.yaml -``` - -### Ingress Gateway - -Ensure all the gateway instances are aware of new components - -```shell -kubectl rollout restart deployment/nginx-ingress-nginx-controller -kubectl rollout status deployment/nginx-ingress-nginx-controller -``` - -## Usage - -To use any of the components you will need the Dapr API token: - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) -``` - -### State - -And POST it to the Dapr API to save your note: - -```shell -curl -X POST \ - -d '[{ "key": "1", "value": "This is my first note" }]' \ - -H "Content-Type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - https://api.cloudylabs.dev/v1.0/state/note-store -``` - -Retrieve the saved note: - -```shell -curl -X GET \ - -H "Content-Type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - https://api.cloudylabs.dev/v1.0/state/note-store/1 -``` - -And now delete the note: - -```shell -curl -X DELETE \ - -H "Content-Type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - https://api.cloudylabs.dev/v1.0/state/note-store/1 -``` - -> For brevity of the example this demo shows only the save, get, delete commands but the Dapr API also includes transactional operations for save and bulk operations for get as well. - - -### Email - -To send email, first edit the [sample email](./sample/email.json) file: - -```json -{ - "operation": "create", - "metadata": { - "emailTo": "daprdemo@chmarny.com", - "subject": "Dapr Demo" - }, - "data": "

Greetings

Hi

" -} -``` - -And POST it to the Dapr API: - -```shell -curl -d @./sample/email.json \ - -H "Content-Type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/bindings/send-email" -``` - -### Twitter - -To query the last 100 tweets for particular query, first edit the [sample query](./sample/twitter.json) file: - -```json -{ - "operation": "get", - "metadata": { - "query": "dapr AND serverless", - "lang": "en", - "result": "recent" - } -} -``` - -Metadata parameters: - -* `query` - can be any valid Twitter query (supports `AND`, `OR` `BUT NOT`, `FROM`, `TO`, `#`, `@`...) -* `lang` - (optional) is the [ISO 639-1](https://meta.wikimedia.org/wiki/Template:List_of_language_names_ordered_by_code) language code -* `result` - (optional) is one of: - * `mixed` - include both popular and real time results in the response - * `recent` - return only the most recent results in the response - * `popular` - return only the most popular results in the response -* `since_id` - (optional) the not inclusive tweet ID query should start from - -And POST it to the Dapr API: - -```shell -curl -d @./sample/twitter.json \ - -H "Content-Type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/bindings/query-twitter" -``` - -And if you have the command-line JSON processor [jq](https://shapeshed.com/jq-json/), you can format the API results. For example, this will display only the ID, Author, and Text of each tweet as a new JSON object: - -```shell -curl -d @./sample/twitter.json \ - -H "Content-Type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/bindings/query-twitter" \ - | jq ".[] | { id: .id_str, user: .user.screen_name, text: .text}" -``` - -The result - -```shell -{ - "id": "1298546227211055109", - "user": "markgossa", - "text": "What a blast! @AzureFunctions Live of August was fully packed with news (new extension bundle, Dapr extension)" -} -{ - "id": "1298181483547357184", - "user": "ysakashita3", - "text": "I submitted a blog post to https://t.co/DXGTgtC4Xc. 'Serverless plugin': #KEDA for scaling down your containers" -} -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Use of Dapr as a component API Server + +This demo users Dapr instance with API token authentication to show the use of Dapr as a API server for any of its 70+ components. To illustrate, this demo will show two use-cases: + +* Simple note management using Redis state store +* Sending email using Sendgrid output binding +* Querying tweets using Twitter bi-directional binding + +## Setup + +### State Component + +Create a `mongo-secret` + +```shell +kubectl create secret generic redis-secret --from-literal=password="" +``` + +Deploy component and [restart gateway](#ingress-gateway) + +```shell +kubectl apply -f config/state.yaml +``` + +### Email Component + +Create a `email-secret` + +```shell +kubectl create secret generic email-secret --from-literal=apiKey="" +``` + +Deploy component and [restart gateway](#ingress-gateway) + +```shell +kubectl apply -f config/email.yaml +``` + +### Twitter Component + +Create a `twitter-secret` + +```shell +kubectl create secret generic twitter-secret \ + --from-literal=consumerKey="" \ + --from-literal=consumerSecret="" \ + --from-literal=accessToken="" \ + --from-literal=accessSecret="" +``` + +Deploy component and [restart gateway](#ingress-gateway) + +```shell +kubectl apply -f config/twitter.yaml +``` + +### Ingress Gateway + +Ensure all the gateway instances are aware of new components + +```shell +kubectl rollout restart deployment/nginx-ingress-nginx-controller +kubectl rollout status deployment/nginx-ingress-nginx-controller +``` + +## Usage + +To use any of the components you will need the Dapr API token: + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) +``` + +### State + +And POST it to the Dapr API to save your note: + +```shell +curl -X POST \ + -d '[{ "key": "1", "value": "This is my first note" }]' \ + -H "Content-Type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + https://api.cloudylabs.dev/v1.0/state/note-store +``` + +Retrieve the saved note: + +```shell +curl -X GET \ + -H "Content-Type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + https://api.cloudylabs.dev/v1.0/state/note-store/1 +``` + +And now delete the note: + +```shell +curl -X DELETE \ + -H "Content-Type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + https://api.cloudylabs.dev/v1.0/state/note-store/1 +``` + +> For brevity of the example this demo shows only the save, get, delete commands but the Dapr API also includes transactional operations for save and bulk operations for get as well. + + +### Email + +To send email, first edit the [sample email](./sample/email.json) file: + +```json +{ + "operation": "create", + "metadata": { + "emailTo": "daprdemo@chmarny.com", + "subject": "Dapr Demo" + }, + "data": "

Greetings

Hi

" +} +``` + +And POST it to the Dapr API: + +```shell +curl -d @./sample/email.json \ + -H "Content-Type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/bindings/send-email" +``` + +### Twitter + +To query the last 100 tweets for particular query, first edit the [sample query](./sample/twitter.json) file: + +```json +{ + "operation": "get", + "metadata": { + "query": "dapr AND serverless", + "lang": "en", + "result": "recent" + } +} +``` + +Metadata parameters: + +* `query` - can be any valid Twitter query (supports `AND`, `OR` `BUT NOT`, `FROM`, `TO`, `#`, `@`...) +* `lang` - (optional) is the [ISO 639-1](https://meta.wikimedia.org/wiki/Template:List_of_language_names_ordered_by_code) language code +* `result` - (optional) is one of: + * `mixed` - include both popular and real time results in the response + * `recent` - return only the most recent results in the response + * `popular` - return only the most popular results in the response +* `since_id` - (optional) the not inclusive tweet ID query should start from + +And POST it to the Dapr API: + +```shell +curl -d @./sample/twitter.json \ + -H "Content-Type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/bindings/query-twitter" +``` + +And if you have the command-line JSON processor [jq](https://shapeshed.com/jq-json/), you can format the API results. For example, this will display only the ID, Author, and Text of each tweet as a new JSON object: + +```shell +curl -d @./sample/twitter.json \ + -H "Content-Type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/bindings/query-twitter" \ + | jq ".[] | { id: .id_str, user: .user.screen_name, text: .text}" +``` + +The result + +```shell +{ + "id": "1298546227211055109", + "user": "markgossa", + "text": "What a blast! @AzureFunctions Live of August was fully packed with news (new extension bundle, Dapr extension)" +} +{ + "id": "1298181483547357184", + "user": "ysakashita3", + "text": "I submitted a blog post to https://t.co/DXGTgtC4Xc. 'Serverless plugin': #KEDA for scaling down your containers" +} +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/component-api/config/email.yaml b/component-api/config/email.yaml index 3a45197..522b5cc 100644 --- a/component-api/config/email.yaml +++ b/component-api/config/email.yaml @@ -1,15 +1,15 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: send-email -spec: - type: bindings.twilio.sendgrid - metadata: - - name: emailFrom - value: "demo@thingz.io" - - name: apiKey - secretKeyRef: - name: email-secret - key: apiKey -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: send-email +spec: + type: bindings.twilio.sendgrid + metadata: + - name: emailFrom + value: "demo@thingz.io" + - name: apiKey + secretKeyRef: + name: email-secret + key: apiKey +scopes: - nginx-ingress \ No newline at end of file diff --git a/component-api/config/state.yaml b/component-api/config/state.yaml index 2098ca6..ed5a291 100644 --- a/component-api/config/state.yaml +++ b/component-api/config/state.yaml @@ -1,15 +1,15 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: note-store -spec: - type: state.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: note-store +spec: + type: state.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password +scopes: - nginx-ingress \ No newline at end of file diff --git a/component-api/config/twitter.yaml b/component-api/config/twitter.yaml index d0e464b..540f6cb 100644 --- a/component-api/config/twitter.yaml +++ b/component-api/config/twitter.yaml @@ -1,25 +1,25 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: query-twitter -spec: - type: bindings.twitter - metadata: - - name: consumerKey - secretKeyRef: - name: twitter-secret - key: consumerKey - - name: consumerSecret - secretKeyRef: - name: twitter-secret - key: consumerSecret - - name: accessToken - secretKeyRef: - name: twitter-secret - key: accessToken - - name: accessSecret - secretKeyRef: - name: twitter-secret - key: accessSecret -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: query-twitter +spec: + type: bindings.twitter + metadata: + - name: consumerKey + secretKeyRef: + name: twitter-secret + key: consumerKey + - name: consumerSecret + secretKeyRef: + name: twitter-secret + key: consumerSecret + - name: accessToken + secretKeyRef: + name: twitter-secret + key: accessToken + - name: accessSecret + secretKeyRef: + name: twitter-secret + key: accessSecret +scopes: - nginx-ingress \ No newline at end of file diff --git a/component-api/sample/email.json b/component-api/sample/email.json index 6cf0b60..1390b58 100644 --- a/component-api/sample/email.json +++ b/component-api/sample/email.json @@ -1,8 +1,8 @@ -{ - "operation": "create", - "metadata": { - "emailTo": "daprdemo@chmarny.com", - "subject": "Dapr Demo" - }, - "data": "

Greetings:

Hi

" +{ + "operation": "create", + "metadata": { + "emailTo": "daprdemo@chmarny.com", + "subject": "Dapr Demo" + }, + "data": "

Greetings:

Hi

" } \ No newline at end of file diff --git a/component-api/sample/twitter.json b/component-api/sample/twitter.json index 450bdf8..cff8822 100644 --- a/component-api/sample/twitter.json +++ b/component-api/sample/twitter.json @@ -1,8 +1,8 @@ -{ - "operation": "get", - "metadata": { - "query": "dapr AND serverless", - "lang": "en", - "result": "recent" - } +{ + "operation": "get", + "metadata": { + "query": "dapr AND serverless", + "lang": "en", + "result": "recent" + } } \ No newline at end of file diff --git a/cron-binding/Dockerfile b/cron-binding/Dockerfile index f5904ac..a5c01c7 100644 --- a/cron-binding/Dockerfile +++ b/cron-binding/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/cron-binding/Makefile b/cron-binding/Makefile index 8e268ab..1b7dfc1 100644 --- a/cron-binding/Makefile +++ b/cron-binding/Makefile @@ -1,58 +1,58 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=cron-binding-demo -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: help tidy build run image lint tag clean -all: help - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -build: tidy ## Builds local release binary - CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . - -debug: ## Runs uncompiled code it in Dapr in debug mode - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 8080 \ - --app-protocol http \ - --components-path ./config \ - go run main.go - -run: build ## Builds binary and runs it in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 8080 \ - --app-protocol http \ - --components-path ./config \ - bin/$(SERVICE_NAME) - -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/component.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/cron-binding-demo - kubectl rollout status deployment/cron-binding-demo - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=cron-binding-demo +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: help tidy build run image lint tag clean +all: help + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +build: tidy ## Builds local release binary + CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . + +debug: ## Runs uncompiled code it in Dapr in debug mode + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 8080 \ + --app-protocol http \ + --components-path ./config \ + go run main.go + +run: build ## Builds binary and runs it in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 8080 \ + --app-protocol http \ + --components-path ./config \ + bin/$(SERVICE_NAME) + +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/component.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/cron-binding-demo + kubectl rollout status deployment/cron-binding-demo + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/cron-binding/README.md b/cron-binding/README.md index 42f6a7f..f667fba 100644 --- a/cron-binding/README.md +++ b/cron-binding/README.md @@ -1,79 +1,79 @@ -# cron-binding - -## Binding - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: run -spec: - type: bindings.cron - metadata: - - name: schedule - value: "@every 3s" -``` - -For more information about this binding see the [Dapr docs](https://github.com/dapr/docs/blob/master/reference/specs/bindings/cron.md) - -## Run - -Dapr cron binding demo in `go`. To use run it, first start the service - -```shell -dapr run --app-id cron-binding-demo \ - --protocol http \ - --app-port 8080 \ - --components-path ./config \ - go run *.go -``` - - -## Deploy - -Deploy and wait for the pod to be ready - -```shell -kubectl apply -f k8s/component.yaml -kubectl apply -f k8s/deployment.yaml -kubectl rollout status deployment/cron-binding-demo -``` - -If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready - -```shell -kubectl rollout restart deployment/cron-binding-demo -kubectl rollout status deployment/cron-binding-demo -``` - -Follow logs to view schedule firing - -```shell -kubectl logs -l app=cron-binding-demo -c daprd -f -``` - -Depending on the frequency you used there may not be an entry right away but you should see something similar to this - -```json -{ - "app_id":"cron-binding-demo", - "instance":"cron-binding-demo-6c88dbb467-j54br", - "level":"debug", - "msg":"next run: 59m59.629538771s", - "scope":"dapr.contrib", - "time":"2020-08-31T13:08:34.37049343Z", - "type":"log", - "ver":"0.10.0" -} -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) - - - +# cron-binding + +## Binding + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: run +spec: + type: bindings.cron + metadata: + - name: schedule + value: "@every 3s" +``` + +For more information about this binding see the [Dapr docs](https://github.com/dapr/docs/blob/master/reference/specs/bindings/cron.md) + +## Run + +Dapr cron binding demo in `go`. To use run it, first start the service + +```shell +dapr run --app-id cron-binding-demo \ + --protocol http \ + --app-port 8080 \ + --components-path ./config \ + go run *.go +``` + + +## Deploy + +Deploy and wait for the pod to be ready + +```shell +kubectl apply -f k8s/component.yaml +kubectl apply -f k8s/deployment.yaml +kubectl rollout status deployment/cron-binding-demo +``` + +If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready + +```shell +kubectl rollout restart deployment/cron-binding-demo +kubectl rollout status deployment/cron-binding-demo +``` + +Follow logs to view schedule firing + +```shell +kubectl logs -l app=cron-binding-demo -c daprd -f +``` + +Depending on the frequency you used there may not be an entry right away but you should see something similar to this + +```json +{ + "app_id":"cron-binding-demo", + "instance":"cron-binding-demo-6c88dbb467-j54br", + "level":"debug", + "msg":"next run: 59m59.629538771s", + "scope":"dapr.contrib", + "time":"2020-08-31T13:08:34.37049343Z", + "type":"log", + "ver":"0.10.0" +} +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) + + + diff --git a/cron-binding/config/cron.yaml b/cron-binding/config/cron.yaml index 141ff78..eab703e 100644 --- a/cron-binding/config/cron.yaml +++ b/cron-binding/config/cron.yaml @@ -1,9 +1,9 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: run -spec: - type: bindings.cron - metadata: - - name: schedule +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: run +spec: + type: bindings.cron + metadata: + - name: schedule value: "@every 3s" \ No newline at end of file diff --git a/cron-binding/go.mod b/cron-binding/go.mod index a3d382e..6a8b48c 100644 --- a/cron-binding/go.mod +++ b/cron-binding/go.mod @@ -1,5 +1,5 @@ -module github.com/mchmarny/dapr-demos/cron-binding - -go 1.15 - -require github.com/dapr/go-sdk v0.11.0 +module github.com/mchmarny/dapr-demos/cron-binding + +go 1.15 + +require github.com/dapr/go-sdk v0.11.0 diff --git a/cron-binding/go.sum b/cron-binding/go.sum index 022b8a1..f05d1cd 100644 --- a/cron-binding/go.sum +++ b/cron-binding/go.sum @@ -1,97 +1,97 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/cron-binding/k8s/component.yaml b/cron-binding/k8s/component.yaml index 7af362e..fea980b 100644 --- a/cron-binding/k8s/component.yaml +++ b/cron-binding/k8s/component.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: schedule -spec: - type: bindings.cron - metadata: - - name: schedule - value: "@every 1h" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: schedule +spec: + type: bindings.cron + metadata: + - name: schedule + value: "@every 1h" +scopes: - cron-binding-demo \ No newline at end of file diff --git a/cron-binding/k8s/deployment.yaml b/cron-binding/k8s/deployment.yaml index ce4a42b..8827e14 100644 --- a/cron-binding/k8s/deployment.yaml +++ b/cron-binding/k8s/deployment.yaml @@ -1,34 +1,34 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cron-binding-demo - labels: - app: cron-binding-demo - demo: cron -spec: - replicas: 1 - selector: - matchLabels: - app: cron-binding-demo - template: - metadata: - labels: - app: cron-binding-demo - demo: cron - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "cron-binding-demo" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8080" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/cron-binding-demo:v0.11.1 - ports: - - containerPort: 8080 - env: - - name: ADDRESS +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cron-binding-demo + labels: + app: cron-binding-demo + demo: cron +spec: + replicas: 1 + selector: + matchLabels: + app: cron-binding-demo + template: + metadata: + labels: + app: cron-binding-demo + demo: cron + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "cron-binding-demo" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8080" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/cron-binding-demo:v0.11.1 + ports: + - containerPort: 8080 + env: + - name: ADDRESS value: ":8080" \ No newline at end of file diff --git a/cron-binding/main.go b/cron-binding/main.go index 2a7a7b8..dbd91b6 100755 --- a/cron-binding/main.go +++ b/cron-binding/main.go @@ -1,48 +1,48 @@ -package main - -import ( - "context" - "log" - - "net/http" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" -) - -var ( - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8080") -) - -func main() { - // create a Dapr service - s := daprd.NewService(address) - - // add some input binding handler - if err := s.AddBindingInvocationHandler("schedule", scheduleHandler); err != nil { - logger.Fatalf("error adding binding handler: %v", err) - } - - // start the service - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("error starting service: %v", err) - } -} - -func scheduleHandler(ctx context.Context, in *common.BindingEvent) (out []byte, err error) { - logger.Printf("Schedule - Metadata:%v, Data:%v", in.Metadata, in.Data) - - // TODO: do something with the cloud event data - - return nil, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + + "net/http" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" +) + +var ( + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8080") +) + +func main() { + // create a Dapr service + s := daprd.NewService(address) + + // add some input binding handler + if err := s.AddBindingInvocationHandler("schedule", scheduleHandler); err != nil { + logger.Fatalf("error adding binding handler: %v", err) + } + + // start the service + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("error starting service: %v", err) + } +} + +func scheduleHandler(ctx context.Context, in *common.BindingEvent) (out []byte, err error) { + logger.Printf("Schedule - Metadata:%v, Data:%v", in.Metadata, in.Data) + + // TODO: do something with the cloud event data + + return nil, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/dapr-aci/README.md b/dapr-aci/README.md index b6b7f25..14b3405 100644 --- a/dapr-aci/README.md +++ b/dapr-aci/README.md @@ -1,174 +1,174 @@ -# Dapr sidecar in ACI - -Demo of Dapr sidecar in Azure Container Instances (ACI) - -## Setup - -This demo illustrates simple service subscription to pub/sub topic and persistence of event payload into state. To deploy Dapr into ACI however you will need to first setup a SMB volume which will be used to store the Dapr components and mounted in the `daprd` container The storage account name needs to be globally unique so set `SNAME` to something 3-24 chars long, containing alphanumerics only, and make sure it's all in lower case. - -```shell -export SNAME="dapraci" -``` - -> assumes your resource group and location defaults are already set. If not, set them now: - -```shell -az account set --subscription -az configure --defaults location= group= -``` - - -Create a storage account - -```shell -az storage account create --name $SNAME --sku Standard_LRS -``` - -Create a storage share - -> For demo purposes share and storage user names are the same - -```shell -az storage share create --name $SNAME --account-name $SNAME -``` - -Capture storage key - -```shell -export ACCOUNT_KEY=$(az storage account keys list --account-name $SNAME \ - --query "[0].value" \ - --output tsv) -echo $ACCOUNT_KEY -``` - -Now update `volumes[components].azureFile.storageAccountKey` in `configuration/app.yaml` file so that ACI can mount it. - -Upload the Dapr component files - -```shell -az storage file upload --account-key $ACCOUNT_KEY \ - --account-name $SNAME \ - --share-name $SNAME \ - --source components/state.yaml - -az storage file upload --account-key $ACCOUNT_KEY \ - --account-name $SNAME \ - --share-name $SNAME \ - --source components/pubsub.yaml -``` - -List files to make sure they are all there - -```shell -az storage file list \ - --account-key $ACCOUNT_KEY \ - --share-name $SNAME \ - --account-name $SNAME \ - --output tsv -``` - -## Deployment - -Once the storage is set up, you can deploy - -```shell -az container create -f deployment/app.yaml -``` - -When you list the containers: - -```shell -az container list -o table -``` - -The result should look something like this: - -```shell -Name ResourceGroup Status Image IP:ports Network CPU/Memory OsType Location --------- --------------- --------- --------------------------------------------------- --------------------- --------- --------------- -------- ---------- -dapraci mchmarny Succeeded daprio/daprd:0.11.3,ghcr.io/mchmarny/aci-app:v0.2.2 40.xx.xx.xx:3500 Public 1.0 core/1.5 gb Linux westus2 -``` - -## Demo - -First, capture the IP for ease of access: - -```shell -export APP_IP=$(az container show -n dapraci --query "ipAddress.ip" -o tsv) -``` - -Next, invoke the ping method thru Dapr API: - -```shell -curl -i -d '{"message":"ping"}' \ - -H "Content-type: application/json" \ - "http://${APP_IP}:3500/v1.0/invoke/dapraci/method/ping" -``` - -Response should look something like this: - -```json -{ "on": 1604003460965972895, "greeting": "pong" } -``` - -You can also invoke the PubSub API on Dapr to publish: - -```shell -curl -i -d '{"message":"hello"}' \ - -H "Content-type: application/json" \ - "http://${APP_IP}:3500/v1.0/publish/pubsub/messages" -``` - -Response from post has no body but you should see the headers: - -```shell -HTTP/1.1 200 OK -Server: fasthttp -Date: Thu, 29 Oct 2020 21:05:43 GMT -Content-Length: 0 -Traceparent: 00-bd0f6f745de1b2cc8b5463f8abaa8656-d2f1d6be6d202273-00 -``` - -This demo also exposes the user container directly. You can disable it but commenting out `- port: 8080` in `ports` section of [configuration/app.yaml](configuration/app.yaml). To invoke the `/ping` route on the deployed app: - -```shell -curl -i -d '{"message":"ping"}' \ - -H "Content-type: application/json" \ - "http://${APP_IP}:8082/ping" -``` - -### Logs - -To view logs from the Dapr container: - -```shell -az container logs --name dapraci --container-name daprd -``` - -> Note, `daprd` is set to log in JSON so you can use `jq` or similar to query the logs and parse out only the messages - -```shell -az container logs --name dapraci --container-name daprd | jq ".msg" -``` - -To query the app container: - -```shell -az container logs --name dapraci --container-name app -``` - -That's it, I hope you found it helpful. - -## Todo - -* Dapr API token auth -* Configuration to show ACT -* Secrets to enable Azure Vault - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Dapr sidecar in ACI + +Demo of Dapr sidecar in Azure Container Instances (ACI) + +## Setup + +This demo illustrates simple service subscription to pub/sub topic and persistence of event payload into state. To deploy Dapr into ACI however you will need to first setup a SMB volume which will be used to store the Dapr components and mounted in the `daprd` container The storage account name needs to be globally unique so set `SNAME` to something 3-24 chars long, containing alphanumerics only, and make sure it's all in lower case. + +```shell +export SNAME="dapraci" +``` + +> assumes your resource group and location defaults are already set. If not, set them now: + +```shell +az account set --subscription +az configure --defaults location= group= +``` + + +Create a storage account + +```shell +az storage account create --name $SNAME --sku Standard_LRS +``` + +Create a storage share + +> For demo purposes share and storage user names are the same + +```shell +az storage share create --name $SNAME --account-name $SNAME +``` + +Capture storage key + +```shell +export ACCOUNT_KEY=$(az storage account keys list --account-name $SNAME \ + --query "[0].value" \ + --output tsv) +echo $ACCOUNT_KEY +``` + +Now update `volumes[components].azureFile.storageAccountKey` in `configuration/app.yaml` file so that ACI can mount it. + +Upload the Dapr component files + +```shell +az storage file upload --account-key $ACCOUNT_KEY \ + --account-name $SNAME \ + --share-name $SNAME \ + --source components/state.yaml + +az storage file upload --account-key $ACCOUNT_KEY \ + --account-name $SNAME \ + --share-name $SNAME \ + --source components/pubsub.yaml +``` + +List files to make sure they are all there + +```shell +az storage file list \ + --account-key $ACCOUNT_KEY \ + --share-name $SNAME \ + --account-name $SNAME \ + --output tsv +``` + +## Deployment + +Once the storage is set up, you can deploy + +```shell +az container create -f deployment/app.yaml +``` + +When you list the containers: + +```shell +az container list -o table +``` + +The result should look something like this: + +```shell +Name ResourceGroup Status Image IP:ports Network CPU/Memory OsType Location +-------- --------------- --------- --------------------------------------------------- --------------------- --------- --------------- -------- ---------- +dapraci mchmarny Succeeded daprio/daprd:0.11.3,ghcr.io/mchmarny/aci-app:v0.2.2 40.xx.xx.xx:3500 Public 1.0 core/1.5 gb Linux westus2 +``` + +## Demo + +First, capture the IP for ease of access: + +```shell +export APP_IP=$(az container show -n dapraci --query "ipAddress.ip" -o tsv) +``` + +Next, invoke the ping method thru Dapr API: + +```shell +curl -i -d '{"message":"ping"}' \ + -H "Content-type: application/json" \ + "http://${APP_IP}:3500/v1.0/invoke/dapraci/method/ping" +``` + +Response should look something like this: + +```json +{ "on": 1604003460965972895, "greeting": "pong" } +``` + +You can also invoke the PubSub API on Dapr to publish: + +```shell +curl -i -d '{"message":"hello"}' \ + -H "Content-type: application/json" \ + "http://${APP_IP}:3500/v1.0/publish/pubsub/messages" +``` + +Response from post has no body but you should see the headers: + +```shell +HTTP/1.1 200 OK +Server: fasthttp +Date: Thu, 29 Oct 2020 21:05:43 GMT +Content-Length: 0 +Traceparent: 00-bd0f6f745de1b2cc8b5463f8abaa8656-d2f1d6be6d202273-00 +``` + +This demo also exposes the user container directly. You can disable it but commenting out `- port: 8080` in `ports` section of [configuration/app.yaml](configuration/app.yaml). To invoke the `/ping` route on the deployed app: + +```shell +curl -i -d '{"message":"ping"}' \ + -H "Content-type: application/json" \ + "http://${APP_IP}:8082/ping" +``` + +### Logs + +To view logs from the Dapr container: + +```shell +az container logs --name dapraci --container-name daprd +``` + +> Note, `daprd` is set to log in JSON so you can use `jq` or similar to query the logs and parse out only the messages + +```shell +az container logs --name dapraci --container-name daprd | jq ".msg" +``` + +To query the app container: + +```shell +az container logs --name dapraci --container-name app +``` + +That's it, I hope you found it helpful. + +## Todo + +* Dapr API token auth +* Configuration to show ACT +* Secrets to enable Azure Vault + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/dapr-aci/components/pubsub.yaml b/dapr-aci/components/pubsub.yaml index df96582..8b19aab 100644 --- a/dapr-aci/components/pubsub.yaml +++ b/dapr-aci/components/pubsub.yaml @@ -1,9 +1,9 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: pubsub -spec: - type: pubsub.azure.servicebus - metadata: - - name: connectionString - value: "Endpoint=sb://.servicebus.windows.net/;SharedAccessKeyName=dapracidemo;SharedAccessKey=;EntityPath=messages" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub +spec: + type: pubsub.azure.servicebus + metadata: + - name: connectionString + value: "Endpoint=sb://.servicebus.windows.net/;SharedAccessKeyName=dapracidemo;SharedAccessKey=;EntityPath=messages" diff --git a/dapr-aci/components/state.yaml b/dapr-aci/components/state.yaml index 749781c..733fa89 100644 --- a/dapr-aci/components/state.yaml +++ b/dapr-aci/components/state.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: store -spec: - type: state.azure.tablestorage - metadata: - - name: accountName - value: dapracidemo - - name: accountKey - value: - - name: tableName - value: dapracidemodata +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: store +spec: + type: state.azure.tablestorage + metadata: + - name: accountName + value: dapracidemo + - name: accountKey + value: + - name: tableName + value: dapracidemodata diff --git a/dapr-aci/deployment/app.yaml b/dapr-aci/deployment/app.yaml index 990d65f..331fd77 100644 --- a/dapr-aci/deployment/app.yaml +++ b/dapr-aci/deployment/app.yaml @@ -1,67 +1,67 @@ -apiVersion: 2018-06-01 -location: westus -name: dapraci -properties: - containers: - - name: app - properties: - image: ghcr.io/mchmarny/aci-app:v0.2.2 - resources: - requests: - cpu: 1.0 - memoryInGB: 1.5 - environmentVariables: - - name: ADDRESS - value: ":8082" - - name: PUBSUB_NAME - value: pubsub - - name: TOPIC_NAME - value: messages - - name: STORE_NAME - value: store - ports: - - port: 8082 - - name: daprd - properties: - image: daprio/daprd:0.11.3 - volumeMounts: - - name: dapr-logs - mountPath: /var/log/pods - - name: component-store - mountPath: /components - command: - - /daprd - - --app-id - - dapraci - - --app-port - - 8082 - - --components-path - - /components - - --log-as-json - - --log-level - - debug - resources: - requests: - cpu: 1.0 - memoryInGB: 1.5 - ports: - - port: 3500 - osType: Linux - restartPolicy: Always - ipAddress: - ports: - # comment the ports you do not want to expose - - port: 8082 - protocol: TCP - - port: 3500 - protocol: TCP - type: Public - volumes: - - name: dapr-logs - emptyDir: {} - - name: component-store - azureFile: - shareName: dapraci - storageAccountName: dapraci - storageAccountKey: +apiVersion: 2018-06-01 +location: westus +name: dapraci +properties: + containers: + - name: app + properties: + image: ghcr.io/mchmarny/aci-app:v0.2.2 + resources: + requests: + cpu: 1.0 + memoryInGB: 1.5 + environmentVariables: + - name: ADDRESS + value: ":8082" + - name: PUBSUB_NAME + value: pubsub + - name: TOPIC_NAME + value: messages + - name: STORE_NAME + value: store + ports: + - port: 8082 + - name: daprd + properties: + image: daprio/daprd:0.11.3 + volumeMounts: + - name: dapr-logs + mountPath: /var/log/pods + - name: component-store + mountPath: /components + command: + - /daprd + - --app-id + - dapraci + - --app-port + - 8082 + - --components-path + - /components + - --log-as-json + - --log-level + - debug + resources: + requests: + cpu: 1.0 + memoryInGB: 1.5 + ports: + - port: 3500 + osType: Linux + restartPolicy: Always + ipAddress: + ports: + # comment the ports you do not want to expose + - port: 8082 + protocol: TCP + - port: 3500 + protocol: TCP + type: Public + volumes: + - name: dapr-logs + emptyDir: {} + - name: component-store + azureFile: + shareName: dapraci + storageAccountName: dapraci + storageAccountKey: type: Microsoft.ContainerInstance/containerGroups \ No newline at end of file diff --git a/dapr-aci/src/Dockerfile b/dapr-aci/src/Dockerfile index cc1ecc6..261c8c3 100644 --- a/dapr-aci/src/Dockerfile +++ b/dapr-aci/src/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.3 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./app . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/app . - -ENTRYPOINT ["./app"] +FROM golang:1.15.3 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./app . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/app . + +ENTRYPOINT ["./app"] diff --git a/dapr-aci/src/Makefile b/dapr-aci/src/Makefile index 19234f8..cc68c74 100644 --- a/dapr-aci/src/Makefile +++ b/dapr-aci/src/Makefile @@ -1,59 +1,59 @@ -IMAGE_NAME ?=aci-app -IMAGE_TAG ?=v0.2.2 -IMAGE_OWNER ?=$(shell git config --get user.username) -APP_ID ?=aci-app -APP_PORT ?=8082 - -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: test -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(APP_ID) \ - --app-port $(APP_PORT) \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ../components \ - --log-level debug \ - go run main.go - -.PHONY: event -event: ## Posts simple event to the messages topic - curl -i -d '{"message":"hello"}' \ - -H "Content-type: application/json" \ - http://localhost:3500/v1.0/publish/pubsub/messages - -.PHONY: ping -ping: ## Invokes app method - curl -i -d '{"message":"ping"}' \ - -H "Content-type: application/json" \ - http://localhost:3500/v1.0/invoke/$(APP_ID)/method/ping - -.PHONY: image -image: tidy ## Builds and publishes image - docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . - docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +IMAGE_NAME ?=aci-app +IMAGE_TAG ?=v0.2.2 +IMAGE_OWNER ?=$(shell git config --get user.username) +APP_ID ?=aci-app +APP_PORT ?=8082 + +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: test +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(APP_ID) \ + --app-port $(APP_PORT) \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ../components \ + --log-level debug \ + go run main.go + +.PHONY: event +event: ## Posts simple event to the messages topic + curl -i -d '{"message":"hello"}' \ + -H "Content-type: application/json" \ + http://localhost:3500/v1.0/publish/pubsub/messages + +.PHONY: ping +ping: ## Invokes app method + curl -i -d '{"message":"ping"}' \ + -H "Content-type: application/json" \ + http://localhost:3500/v1.0/invoke/$(APP_ID)/method/ping + +.PHONY: image +image: tidy ## Builds and publishes image + docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . + docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/dapr-aci/src/go.mod b/dapr-aci/src/go.mod index 25325ab..f957d77 100644 --- a/dapr-aci/src/go.mod +++ b/dapr-aci/src/go.mod @@ -1,12 +1,12 @@ -module github.com/mchmarny/dapr-demos/dapr-aci/src - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.1 - github.com/pkg/errors v0.9.1 - golang.org/x/net v0.0.0-20201029055024-942e2f445f3c // indirect - golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 // indirect - golang.org/x/text v0.3.4 // indirect - google.golang.org/genproto v0.0.0-20201028140639-c77dae4b0522 // indirect -) +module github.com/mchmarny/dapr-demos/dapr-aci/src + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.1 + github.com/pkg/errors v0.9.1 + golang.org/x/net v0.0.0-20201029055024-942e2f445f3c // indirect + golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 // indirect + golang.org/x/text v0.3.4 // indirect + google.golang.org/genproto v0.0.0-20201028140639-c77dae4b0522 // indirect +) diff --git a/dapr-aci/src/go.sum b/dapr-aci/src/go.sum index b76bc84..9884d14 100644 --- a/dapr-aci/src/go.sum +++ b/dapr-aci/src/go.sum @@ -1,122 +1,122 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= -github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= -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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= -golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201029055024-942e2f445f3c h1:rpcgRPA7OvNEOdprt2Wx8/Re2cBTd8NPo/lvo3AyMqk= -golang.org/x/net v0.0.0-20201029055024-942e2f445f3c/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201028140639-c77dae4b0522 h1:7RoRaOmOAXwqnurgQ5g5/d0yCi9ha2UxuTZULXudK7A= -google.golang.org/genproto v0.0.0-20201028140639-c77dae4b0522/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= +github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= +golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201029055024-942e2f445f3c h1:rpcgRPA7OvNEOdprt2Wx8/Re2cBTd8NPo/lvo3AyMqk= +golang.org/x/net v0.0.0-20201029055024-942e2f445f3c/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201028140639-c77dae4b0522 h1:7RoRaOmOAXwqnurgQ5g5/d0yCi9ha2UxuTZULXudK7A= +google.golang.org/genproto v0.0.0-20201028140639-c77dae4b0522/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/dapr-aci/src/main.go b/dapr-aci/src/main.go index d8f79ac..f705fb7 100644 --- a/dapr-aci/src/main.go +++ b/dapr-aci/src/main.go @@ -1,100 +1,100 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "time" - - "net/http" - "os" - "strings" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" - "github.com/pkg/errors" -) - -const ( - addInvokeHandlerError = "error adding invocation handler" - startingServiceError = "error starting service" - clientCreateError = "error creating Dapr client" - addSubscriptionError = "error subscribing to a topic" - addInvocationError = "error creating invcation handler" - methodName = "ping" -) - -var ( - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8082") - - pubSubName = getEnvVar("PUBSUB_NAME", "pubsub") - topicName = getEnvVar("TOPIC_NAME", "messages") - - storeName = getEnvVar("STORE_NAME", "store") - - client dapr.Client -) - -func main() { - s := daprd.NewService(address) - - var clientErr error - if client, clientErr = dapr.NewClient(); clientErr != nil { - logger.Fatalf("%s: %v", clientCreateError, clientErr) - } - defer client.Close() - - if err := s.AddServiceInvocationHandler(methodName, invokeHandler); err != nil { - logger.Fatalf("%s: %v", addInvocationError, err) - } - - subscription := &common.Subscription{ - PubsubName: pubSubName, - Topic: topicName, - Route: fmt.Sprintf("/%s", topicName), - } - - if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { - logger.Fatalf("%s: %v", addSubscriptionError, err) - } - - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("%s: %v", startingServiceError, err) - } -} - -func invokeHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - logger.Printf("Method %s invoked (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", - methodName, in.ContentType, in.Verb, in.QueryString, in.Data) - j := []byte(fmt.Sprintf(`{"on": %d, "greeting": "pong"}`, time.Now().UTC().UnixNano())) - out = &common.Content{ContentType: in.ContentType, Data: j} - return -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf("Event received (PubsubName:%s, Topic:%s, Data: %v", e.PubsubName, e.Topic, e.Data) - - data, ok := e.Data.([]byte) - if !ok { - data, err = json.Marshal(e.Data) - if err != nil { - return false, errors.Wrapf(err, "invalid data format: %T", e.Data) - } - } - - if err := client.SaveState(ctx, storeName, e.ID, data); err != nil { - return false, errors.Wrapf(err, "error saving data to %s (%s)", storeName, data) - } - - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + "time" + + "net/http" + "os" + "strings" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" + "github.com/pkg/errors" +) + +const ( + addInvokeHandlerError = "error adding invocation handler" + startingServiceError = "error starting service" + clientCreateError = "error creating Dapr client" + addSubscriptionError = "error subscribing to a topic" + addInvocationError = "error creating invcation handler" + methodName = "ping" +) + +var ( + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8082") + + pubSubName = getEnvVar("PUBSUB_NAME", "pubsub") + topicName = getEnvVar("TOPIC_NAME", "messages") + + storeName = getEnvVar("STORE_NAME", "store") + + client dapr.Client +) + +func main() { + s := daprd.NewService(address) + + var clientErr error + if client, clientErr = dapr.NewClient(); clientErr != nil { + logger.Fatalf("%s: %v", clientCreateError, clientErr) + } + defer client.Close() + + if err := s.AddServiceInvocationHandler(methodName, invokeHandler); err != nil { + logger.Fatalf("%s: %v", addInvocationError, err) + } + + subscription := &common.Subscription{ + PubsubName: pubSubName, + Topic: topicName, + Route: fmt.Sprintf("/%s", topicName), + } + + if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { + logger.Fatalf("%s: %v", addSubscriptionError, err) + } + + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("%s: %v", startingServiceError, err) + } +} + +func invokeHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + logger.Printf("Method %s invoked (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", + methodName, in.ContentType, in.Verb, in.QueryString, in.Data) + j := []byte(fmt.Sprintf(`{"on": %d, "greeting": "pong"}`, time.Now().UTC().UnixNano())) + out = &common.Content{ContentType: in.ContentType, Data: j} + return +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf("Event received (PubsubName:%s, Topic:%s, Data: %v", e.PubsubName, e.Topic, e.Data) + + data, ok := e.Data.([]byte) + if !ok { + data, err = json.Marshal(e.Data) + if err != nil { + return false, errors.Wrapf(err, "invalid data format: %T", e.Data) + } + } + + if err := client.SaveState(ctx, storeName, e.ID, data); err != nil { + return false, errors.Wrapf(err, "error saving data to %s (%s)", storeName, data) + } + + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/dapr-api-on-aci/README.md b/dapr-api-on-aci/README.md index 3571a03..62a8b62 100644 --- a/dapr-api-on-aci/README.md +++ b/dapr-api-on-aci/README.md @@ -1,129 +1,129 @@ -# Dapr API in ACI - -* Purpose-configured instance of Dapr deployed into Azure Container Instances (ACI) with API token authentication using single command -* Use of Dapr output binding + Dapr as a microservice (in this case email sending) - - -## Setup - -The storage account name needs to be globally unique. Set `SNAME` to something 3-24 chars long, containing alphanumerics only, and make sure it's all in lower case. - -```shell -export SNAME="demodapr" -``` - -> assumes your resource group and location defaults are already set. If not, set them now: - -```shell -az account set --subscription -az configure --defaults location= group= -``` - -Create a storage account - -```shell -az storage account create --name $SNAME --sku Standard_LRS -``` - -Create a storage share for config - -> For demo purposes share and storage user names are the same - -```shell -az storage share create --name $SNAME --account-name $SNAME -``` - -Capture storage key - -```shell -export SKEY=$(az storage account keys list --account-name $SNAME --query "[0].value" --output tsv) -``` - -Create a storage directory for config files - -```shell -az storage directory create --account-name $SNAME --name $SNAME --share-name $SNAME -``` - -Upload the Dapr component files - -> TODO: Make sure you set the Sendgrid API key in the email.yaml - -```shell -az storage file upload --account-name $SNAME --share-name $SNAME --source email.yaml -``` - -## Deployment - -Once the storage is set up, you can deploy. Start by exporting Dapr API Authentication token - -```shell -export DTOKEN=$(openssl rand -base64 36) -``` - -> Note, make sure to save the value exported into `$DTOKEN` variable to ensure you can use it in other terminal sessions. That value will not be recoverable from the ACI service. - -And launch the Dapr container - -```shell -az container create \ - --name $SNAME \ - --ports 3500 \ - --protocol TCP \ - --dns-name-label $SNAME \ - --image docker.io/daprio/daprd:0.11.0 \ - --command-line "/daprd --components-path /components --app-protocol http" \ - --secure-environment-variables "DAPR_API_TOKEN=${DTOKEN}" \ - --azure-file-volume-share-name $SNAME \ - --azure-file-volume-account-name $SNAME \ - --azure-file-volume-account-key $SKEY \ - --azure-file-volume-mount-path /components -``` - -Then check on the status of the deployment - -```shell -az container list -o table -``` - -The result should look something like this - -```shell -Name ResourceGroup Status Image IP:ports Network CPU/Memory OsType Location --------- ------------- --------- ----------------------------- ----------------- ------- --------------- -------- -------- -demodapr mchmarny Succeeded docker.io/daprio/daprd:0.11.0 51.143.49.0:3500 Public 1.0 core/1.5 gb Linux westus2 -``` - -If everything went OK, you should be able post to the email output binding below - -To restart the service after update of environment variables - -```shell -az container restart --name $SNAME -``` - -## Use - -To use the above deployed instance of Dapr configured with SendGrid output binding, POST to the Dapr API following message using `curl`. - -> Note, the from, to, and email subject are configured server side so all you have to submit is a valid output binding message with the `operation` and `data` properties, with the body of the email sent to the user. - -```shell -export SREGION=$(az container list --query "[?contains(name, '${SNAME}')].location" --output tsv) -``` - - -```shell -curl -v -X POST -H "Content-Type: application/json" \ - -H "dapr-api-token: ${DTOKEN}" \ - "http://${SNAME}.${SREGION}.azurecontainer.io:3500/v1.0/bindings/email" \ - -d '{ "operation": "create", "data": "

Test Headline

Test message

"}' -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Dapr API in ACI + +* Purpose-configured instance of Dapr deployed into Azure Container Instances (ACI) with API token authentication using single command +* Use of Dapr output binding + Dapr as a microservice (in this case email sending) + + +## Setup + +The storage account name needs to be globally unique. Set `SNAME` to something 3-24 chars long, containing alphanumerics only, and make sure it's all in lower case. + +```shell +export SNAME="demodapr" +``` + +> assumes your resource group and location defaults are already set. If not, set them now: + +```shell +az account set --subscription +az configure --defaults location= group= +``` + +Create a storage account + +```shell +az storage account create --name $SNAME --sku Standard_LRS +``` + +Create a storage share for config + +> For demo purposes share and storage user names are the same + +```shell +az storage share create --name $SNAME --account-name $SNAME +``` + +Capture storage key + +```shell +export SKEY=$(az storage account keys list --account-name $SNAME --query "[0].value" --output tsv) +``` + +Create a storage directory for config files + +```shell +az storage directory create --account-name $SNAME --name $SNAME --share-name $SNAME +``` + +Upload the Dapr component files + +> TODO: Make sure you set the Sendgrid API key in the email.yaml + +```shell +az storage file upload --account-name $SNAME --share-name $SNAME --source email.yaml +``` + +## Deployment + +Once the storage is set up, you can deploy. Start by exporting Dapr API Authentication token + +```shell +export DTOKEN=$(openssl rand -base64 36) +``` + +> Note, make sure to save the value exported into `$DTOKEN` variable to ensure you can use it in other terminal sessions. That value will not be recoverable from the ACI service. + +And launch the Dapr container + +```shell +az container create \ + --name $SNAME \ + --ports 3500 \ + --protocol TCP \ + --dns-name-label $SNAME \ + --image docker.io/daprio/daprd:0.11.0 \ + --command-line "/daprd --components-path /components --app-protocol http" \ + --secure-environment-variables "DAPR_API_TOKEN=${DTOKEN}" \ + --azure-file-volume-share-name $SNAME \ + --azure-file-volume-account-name $SNAME \ + --azure-file-volume-account-key $SKEY \ + --azure-file-volume-mount-path /components +``` + +Then check on the status of the deployment + +```shell +az container list -o table +``` + +The result should look something like this + +```shell +Name ResourceGroup Status Image IP:ports Network CPU/Memory OsType Location +-------- ------------- --------- ----------------------------- ----------------- ------- --------------- -------- -------- +demodapr mchmarny Succeeded docker.io/daprio/daprd:0.11.0 51.143.49.0:3500 Public 1.0 core/1.5 gb Linux westus2 +``` + +If everything went OK, you should be able post to the email output binding below + +To restart the service after update of environment variables + +```shell +az container restart --name $SNAME +``` + +## Use + +To use the above deployed instance of Dapr configured with SendGrid output binding, POST to the Dapr API following message using `curl`. + +> Note, the from, to, and email subject are configured server side so all you have to submit is a valid output binding message with the `operation` and `data` properties, with the body of the email sent to the user. + +```shell +export SREGION=$(az container list --query "[?contains(name, '${SNAME}')].location" --output tsv) +``` + + +```shell +curl -v -X POST -H "Content-Type: application/json" \ + -H "dapr-api-token: ${DTOKEN}" \ + "http://${SNAME}.${SREGION}.azurecontainer.io:3500/v1.0/bindings/email" \ + -d '{ "operation": "create", "data": "

Test Headline

Test message

"}' +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/dapr-api-on-aci/email.yaml b/dapr-api-on-aci/email.yaml index 2c46019..8790f67 100644 --- a/dapr-api-on-aci/email.yaml +++ b/dapr-api-on-aci/email.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: email -spec: - type: bindings.twilio.sendgrid - metadata: - - name: emailFrom - value: "demo@thingz.io" - - name: subject - value: "Demo Email Subject" - - name: apiKey +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: email +spec: + type: bindings.twilio.sendgrid + metadata: + - name: emailFrom + value: "demo@thingz.io" + - name: subject + value: "Demo Email Subject" + - name: apiKey value: \ No newline at end of file diff --git a/daprized-ingress/README.md b/daprized-ingress/README.md index 9273928..12e2b9b 100644 --- a/daprized-ingress/README.md +++ b/daprized-ingress/README.md @@ -1,166 +1,166 @@ -# Dapr API on Cluster Ingress Controller - -This how to will walk through the process of configuring Dapr api on your cluster ingress. The instructions are pretty much the same regardless of the type of ingress you are using. In this how to I'll be using [NGINX](https://nginx.org/en/). - -![](img/diagram.png) - -> This how-to assumes you already have Dapr installed in your cluster. If not, consider the opinionated install [here](../setup) or the fully documented instruction in [Dapr docs](https://docs.dapr.io/operations/hosting/kubernetes/). - -## Setup - -To make this how-to more reproducible, start by defining the namespace where your NGINX ingress is/will be located: - -```shell -export INGRESS_NAMESPACE="default" -``` - -If you are not using `default` namespace, apply also the necessary roles to that namespace: - -```shell -kubectl apply -f config/namespace.yml -n $INGRESS_NAMESPACE -``` - -Next, create a secret to hold the Dapr API token: - -```shell -export API_TOKEN=$(openssl rand -base64 32) -kubectl create secret generic dapr-api-token --from-literal=token="${API_TOKEN}" -n $INGRESS_NAMESPACE -``` - -## Deployment - -Next, apply the Dapr config for your ingress (this holds the tracing configuration as well as ensures that the ingress is able to access only the `dapr-api-token` secret we specified above): - -```shell -kubectl apply -f config/ingress-config.yaml -n $INGRESS_NAMESPACE -``` - -Add the Helm repo: - -```shell -helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx -helm repo update -``` - -Install NGINX: - -> Note, the following command creates 2 replicas to prevent ingress issues during subsequent changes - -```shell -helm install nginx ingress-nginx/ingress-nginx \ - --set controller.replicaCount=2 \ - -f config/ingress-annotations.yaml \ - -n $INGRESS_NAMESPACE -``` - -And wait for the deployment to finish: - -```shell -kubectl rollout status deployment/nginx-ingress-nginx-controller -n $INGRESS_NAMESPACE -``` - -Notice we are adding additional configuration which appends all the necessary pod annotations to dapr'ize the ingress controller. Here is the complete list of annotations that were used in above command: - -```yaml -controller: - podAnnotations: - dapr.io/enabled: "true" - dapr.io/app-id: "nginx-ingress" - dapr.io/app-protocol: "http" - dapr.io/app-port: "80" - dapr.io/api-token-secret: "dapr-api-token" - dapr.io/config: "ingress-config" - dapr.io/log-as-json: "true" -``` - -Most of these are self explanatory, including references to both the API token (secret) and ingress config we've created above. You can learn more about all of the annotations supported in Dapr [here](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-annotations/): - - -## Configure - -Finally, to expose the Dapr API, first change the host name (`api.thingz.io`) in [config/ingress.yaml](config/ingress.yaml) file to the domain name you would like to use: - -> Note, you will have to create an A entry in your DNS server in the following step so use a domain you can actually control. `thingz.io` is mine ;) - -```yaml -spec: - rules: - - host: api.thingz.io - http: - paths: - - path: / - backend: - serviceName: nginx-ingress-dapr - servicePort: 80 -``` - -When done, apply the ingress to the cluster: - -```shell -kubectl apply -f config/ingress.yaml -n $INGRESS_NAMESPACE -``` - -> See [setup](../setup) for instructions hot to create TLS certificates and configure SSL for your domain. - -## DNS - -Now that everything is set up, the only thing that's left is to create the DNS entry for your cluster ingress controller. To do that, start by obtaining the public IP address on your ingress: - -```shell -kubectl get svc nginx-ingress-nginx-controller \ - -o jsonpath='{.status.loadBalancer.ingress[0].ip}' \ - -n $INGRESS_NAMESPACE -``` - -Next go to your DNS service dashboard and create an `A` entry for that IP. For example, if your domain you entered in the above Configuration step was `api.thingz.io` and your IP address printed above was `1.2.3.4`, you would create a following entry: - -```shell -Name: Type: TTL: Data: -api A 60s 1.2.3.4 -``` - -The `name` (or `api` in my example above) is basically whatever you want as long as it is the same name you used in the Configuration step above. Also, the `TTL` setting is key as you don't want to wait for long time to have this entry propagate. Some DNS services will have different duration formats, so just aim for the lowest number possible like `1m`. - -When done, you can test if the DNS is ready by running `dig` with your domain name (e.g. `api.thingz.io`): - -```shell -dig -``` - -When the A record for your domain name in reflected in the ANSWER SECTION you are ready to test. - -## Test - -In case you lost the terminal session when we first defined the `API_TOKEN` variable, capture it again: - - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" -n ${INGRESS_NAMESPACE} | base64 --decode) -``` - -Then curl the Dapr health API to ensure ensure everything works. Just make sure to replace the `` with your actual domain. - -```shell -curl -i \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "http:///v1.0/healthz" -``` - -If the response from the Dapr health API is `HTTP/2 200` you are good to go. Check the [hardened demo](../hardened) for example how you can use this API to access services in other namespaces, how to control access to all your Dapr services in the cluster, and how you can get an end-to-end traceability for all invocations including the ingress. - -## Troubleshooting - -There are a few places to check if you experience any issues: - -* Does the domain name you used resolve to the cluster ingress IP? -* Has Dapr injected its sidecar into the ingress pod? (Container count on `get pods` should be 2) - - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Dapr API on Cluster Ingress Controller + +This how to will walk through the process of configuring Dapr api on your cluster ingress. The instructions are pretty much the same regardless of the type of ingress you are using. In this how to I'll be using [NGINX](https://nginx.org/en/). + +![](img/diagram.png) + +> This how-to assumes you already have Dapr installed in your cluster. If not, consider the opinionated install [here](../setup) or the fully documented instruction in [Dapr docs](https://docs.dapr.io/operations/hosting/kubernetes/). + +## Setup + +To make this how-to more reproducible, start by defining the namespace where your NGINX ingress is/will be located: + +```shell +export INGRESS_NAMESPACE="default" +``` + +If you are not using `default` namespace, apply also the necessary roles to that namespace: + +```shell +kubectl apply -f config/namespace.yml -n $INGRESS_NAMESPACE +``` + +Next, create a secret to hold the Dapr API token: + +```shell +export API_TOKEN=$(openssl rand -base64 32) +kubectl create secret generic dapr-api-token --from-literal=token="${API_TOKEN}" -n $INGRESS_NAMESPACE +``` + +## Deployment + +Next, apply the Dapr config for your ingress (this holds the tracing configuration as well as ensures that the ingress is able to access only the `dapr-api-token` secret we specified above): + +```shell +kubectl apply -f config/ingress-config.yaml -n $INGRESS_NAMESPACE +``` + +Add the Helm repo: + +```shell +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx +helm repo update +``` + +Install NGINX: + +> Note, the following command creates 2 replicas to prevent ingress issues during subsequent changes + +```shell +helm install nginx ingress-nginx/ingress-nginx \ + --set controller.replicaCount=2 \ + -f config/ingress-annotations.yaml \ + -n $INGRESS_NAMESPACE +``` + +And wait for the deployment to finish: + +```shell +kubectl rollout status deployment/nginx-ingress-nginx-controller -n $INGRESS_NAMESPACE +``` + +Notice we are adding additional configuration which appends all the necessary pod annotations to dapr'ize the ingress controller. Here is the complete list of annotations that were used in above command: + +```yaml +controller: + podAnnotations: + dapr.io/enabled: "true" + dapr.io/app-id: "nginx-ingress" + dapr.io/app-protocol: "http" + dapr.io/app-port: "80" + dapr.io/api-token-secret: "dapr-api-token" + dapr.io/config: "ingress-config" + dapr.io/log-as-json: "true" +``` + +Most of these are self explanatory, including references to both the API token (secret) and ingress config we've created above. You can learn more about all of the annotations supported in Dapr [here](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-annotations/): + + +## Configure + +Finally, to expose the Dapr API, first change the host name (`api.thingz.io`) in [config/ingress.yaml](config/ingress.yaml) file to the domain name you would like to use: + +> Note, you will have to create an A entry in your DNS server in the following step so use a domain you can actually control. `thingz.io` is mine ;) + +```yaml +spec: + rules: + - host: api.thingz.io + http: + paths: + - path: / + backend: + serviceName: nginx-ingress-dapr + servicePort: 80 +``` + +When done, apply the ingress to the cluster: + +```shell +kubectl apply -f config/ingress.yaml -n $INGRESS_NAMESPACE +``` + +> See [setup](../setup) for instructions hot to create TLS certificates and configure SSL for your domain. + +## DNS + +Now that everything is set up, the only thing that's left is to create the DNS entry for your cluster ingress controller. To do that, start by obtaining the public IP address on your ingress: + +```shell +kubectl get svc nginx-ingress-nginx-controller \ + -o jsonpath='{.status.loadBalancer.ingress[0].ip}' \ + -n $INGRESS_NAMESPACE +``` + +Next go to your DNS service dashboard and create an `A` entry for that IP. For example, if your domain you entered in the above Configuration step was `api.thingz.io` and your IP address printed above was `1.2.3.4`, you would create a following entry: + +```shell +Name: Type: TTL: Data: +api A 60s 1.2.3.4 +``` + +The `name` (or `api` in my example above) is basically whatever you want as long as it is the same name you used in the Configuration step above. Also, the `TTL` setting is key as you don't want to wait for long time to have this entry propagate. Some DNS services will have different duration formats, so just aim for the lowest number possible like `1m`. + +When done, you can test if the DNS is ready by running `dig` with your domain name (e.g. `api.thingz.io`): + +```shell +dig +``` + +When the A record for your domain name in reflected in the ANSWER SECTION you are ready to test. + +## Test + +In case you lost the terminal session when we first defined the `API_TOKEN` variable, capture it again: + + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" -n ${INGRESS_NAMESPACE} | base64 --decode) +``` + +Then curl the Dapr health API to ensure ensure everything works. Just make sure to replace the `` with your actual domain. + +```shell +curl -i \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "http:///v1.0/healthz" +``` + +If the response from the Dapr health API is `HTTP/2 200` you are good to go. Check the [hardened demo](../hardened) for example how you can use this API to access services in other namespaces, how to control access to all your Dapr services in the cluster, and how you can get an end-to-end traceability for all invocations including the ingress. + +## Troubleshooting + +There are a few places to check if you experience any issues: + +* Does the domain name you used resolve to the cluster ingress IP? +* Has Dapr injected its sidecar into the ingress pod? (Container count on `get pods` should be 2) + + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/daprized-ingress/config/ingress-annotations.yaml b/daprized-ingress/config/ingress-annotations.yaml index c81e17f..5bb0d52 100644 --- a/daprized-ingress/config/ingress-annotations.yaml +++ b/daprized-ingress/config/ingress-annotations.yaml @@ -1,9 +1,9 @@ -controller: - podAnnotations: - dapr.io/enabled: "true" - dapr.io/app-id: "nginx-ingress" - dapr.io/app-protocol: "http" - dapr.io/app-port: "80" - dapr.io/api-token-secret: "dapr-api-token" - dapr.io/config: "ingress-config" +controller: + podAnnotations: + dapr.io/enabled: "true" + dapr.io/app-id: "nginx-ingress" + dapr.io/app-protocol: "http" + dapr.io/app-port: "80" + dapr.io/api-token-secret: "dapr-api-token" + dapr.io/config: "ingress-config" dapr.io/log-as-json: "true" \ No newline at end of file diff --git a/daprized-ingress/config/ingress-config.yaml b/daprized-ingress/config/ingress-config.yaml index 2e5f302..64ef501 100644 --- a/daprized-ingress/config/ingress-config.yaml +++ b/daprized-ingress/config/ingress-config.yaml @@ -1,21 +1,25 @@ ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: ingress-config -spec: - tracing: - samplingRate: "1" - secrets: - scopes: - - storeName: kubernetes - defaultAccess: deny - allowedSecrets: ["dapr-api-token"] ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: tracing -spec: - tracing: - samplingRate: "1" \ No newline at end of file +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: ingress-config +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" + secrets: + scopes: + - storeName: kubernetes + defaultAccess: deny + allowedSecrets: ["dapr-api-token"] +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: tracing +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" diff --git a/daprized-ingress/config/ingress.yaml b/daprized-ingress/config/ingress.yaml index ec855dd..a806e0f 100644 --- a/daprized-ingress/config/ingress.yaml +++ b/daprized-ingress/config/ingress.yaml @@ -1,17 +1,17 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: ingress-rules - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/rewrite-target: / -spec: - rules: - - host: api.thingz.io - http: - paths: - - path: / - backend: - serviceName: nginx-ingress-dapr - servicePort: 80 +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: ingress-rules + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: api.mfussell.com + http: + paths: + - path: / + backend: + serviceName: nginx-ingress-dapr + servicePort: 80 \ No newline at end of file diff --git a/daprized-ingress/config/namespace.yml b/daprized-ingress/config/namespace.yml index fe2d6b0..73b0485 100644 --- a/daprized-ingress/config/namespace.yml +++ b/daprized-ingress/config/namespace.yml @@ -1,20 +1,20 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: secret-reader -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: dapr-secret-reader -subjects: -- kind: ServiceAccount - name: default -roleRef: - kind: Role - name: secret-reader +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secret-reader +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dapr-secret-reader +subjects: +- kind: ServiceAccount + name: default +roleRef: + kind: Role + name: secret-reader apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/fan-out/README.md b/fan-out/README.md index a48492d..cc69bff 100644 --- a/fan-out/README.md +++ b/fan-out/README.md @@ -1,151 +1,151 @@ -# fan-out demo - -`Fan-out` is a messaging pattern where single message source is "broadcasted" to multiple targets. The common use-case for this may be situation where multiple teams or systems need to receive events from the same source. This is sometimes made even more complicated by the differences in expected formats and protocols by each one of the target systems. - -This demo will illustrate how to use Dapr's plugable component mechanism to `fan-out` events from one Pub/Sub configured with Redis to: - -* Kafka topic in CSV format -* REST endpoint in JSON format -* gRPC service in XML format - -![](./img/fan-out-in-dapr.png) - -This allows for incremental modifications with ability to customize each stream according to its unique configuration needs (e.g. throughout, authentication, format, retry strategy, or even error tolerance). For more information about Dapr's pub/sub see these [docs](https://github.com/dapr/docs/tree/master/concepts/publish-subscribe-messaging) - -This demo requires Dapr `v0.11` as well as go `1.14+` and docker-compose `v1.26+` - -> Note, the version of the demo with Event Hub binding has been moved to [this branch](https://github.com/mchmarny/dapr-demos/tree/fanout-eventhubs/fan-out) - -## App 2: Pub/Sub to Pub/Sub Publisher - -To run these demos you will need access to Redis and Kafka servers. For Redis, you can use the one installed during local Dapr setup. For Kafka, you can use the included Docker Compose file. First, navigate to the `queue-format-converter` and start Kafka: - -```shell -docker-compose -f ./config/kafka.yaml up -d -``` - -The result should look something like this: - -```shell -Creating network "config_default" with the default driver -Creating config_kafka_1 ... done -Creating config_zookeeper_1 ... done -``` - -Now, start `App 2` which will receive events from Redis, convert them to XML, and publish them onto the Kafka topic: - -```shell -dapr run \ - --app-id app2 \ - --app-port 60010 \ - --app-protocol grpc \ - --components-path ./config \ - go run main.go -``` - -> The source and targets Pub/Sub components are defined in the [./config](./queue-format-converter/config) directory in their respected files so the use code is free of SDK and libraries which allows for easy re-configuration at run-time. - -Leave the application running, we will come back to it after configuring `App 1` - -## App 1: Pub/Sub Event Producer - -To demo the above `App 2` we will need events. To produce events, in another terminal session navigate to `queue-event-producer` directory, start the `App 1`: - -```shell -dapr run \ - --app-id app1 \ - --app-port 60013 \ - --app-protocol grpc \ - --components-path ./config \ - go run main.go -``` - -The app will now publish one event every `3s`. To change the frequency just define the desired duration using `THREAD_PUB_FREQ` variable and restart the app. The results should look something like this: - -```shell -== APP == published: {"id":"df50a6c7-b5bb-45ce-b3a8-ad428bbbd5fe","temperature":60.46998219508215,"humidity":94.05150371362079,"time":1598960035} -``` - -Now in the `App 1`, the log output for each event should look something like this: - -```shell -== APP == Event - PubsubName:fanout-source-pubsub, Topic:events, ID:5ffa4502-8bf1-4bbf-927e-8b62e1949166 -== APP == Target (csv): "45ecf820-705b-47c2-a2e4-7dbb3eecb728",66.459360,43.777042,"2020-09-01T04:33:58-07:00" -``` - -## App 3: Pub/Sub to External REST Endpoint Publisher - -To add another publisher which converts the received events into JSON and publishes them to the component defined REST endpoint, first navigate to the `http-format-converter` directory and start `App 3`: - -```shell -dapr run \ - --app-id app3 \ - --app-port 60011 \ - --app-protocol grpc \ - --components-path ./config \ - go run main.go -``` - -> This app is configured with an HTTP binding that defines the target and verb of the invocation. This external configuration allows for easy change of the target binding to something like email using SendGrid or AWS S3 without changing the use-code. To learn more about Dapr bindings, see these [docs](https://github.com/dapr/docs/tree/master/concepts/bindings#supported-bindings-and-specs) - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-http-target-post-binding -spec: - type: bindings.http - metadata: - - name: url - value: https://postman-echo.com/post - - name: method - value: POST -``` - -If the `App 1` is still running, you should see entries in the log similar to this: - -```shell -== APP == Event - PubsubName:fanout-source-pubsub, Topic:events, ID:d5bbcc9e-f0d3-46df-8d8e-f7dadb3f304c -== APP == Target (json): {"temperature":20.326656,"humidity":36.093533,"time":1598961599,"id":"b2fa85cf-1be6-4489-9aec-613cf969675e"} -``` - -## App 4: Pub/Sub to another Dapr Service Publisher - -To add the final publisher, which will convert events into XML format and publish them to another Dapr service over gRPC, first navigate to the `grpc-echo-service` directory and start target service. For demo purposes, we will use the included echo service which simply returns whatever message it receives. - -```shell -dapr run \ - --app-id grpc-echo-service \ - --app-port 60015 \ - --app-protocol grpc \ - go run main.go -``` - -Then, in yet another terminal session, navigate to the `service-format-converter` directory and start the `App 4` that will invoke the `grpc-echo-service`: - -```shell -dapr run \ - --app-id app4 \ - --app-port 60012 \ - --app-protocol grpc \ - --components-path ./config \ - go run main.go -``` - -If everything went well, you should see something similar in the `App 4` logs: - -```shell -== APP == Target: &{Data:[60 84 111 112 105 99 69 118...] ContentType:application/xml} -== APP == Response: df450605-4927-407e-a2b2-61233939ee581.0com.dapr.event.sentapp1application/json{"time":1598962950,"id":"745e3ebe-990b-4292-9347-f84ff81f41e6","temperature":31.812637,"humidity":46.894296}eventsfanout-source-pubsub -``` - -> If you left all the apps running, you can go to each terminal session and see the different formats which are generated and published by each application. - - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# fan-out demo + +`Fan-out` is a messaging pattern where single message source is "broadcasted" to multiple targets. The common use-case for this may be situation where multiple teams or systems need to receive events from the same source. This is sometimes made even more complicated by the differences in expected formats and protocols by each one of the target systems. + +This demo will illustrate how to use Dapr's plugable component mechanism to `fan-out` events from one Pub/Sub configured with Redis to: + +* Kafka topic in CSV format +* REST endpoint in JSON format +* gRPC service in XML format + +![](./img/fan-out-in-dapr.png) + +This allows for incremental modifications with ability to customize each stream according to its unique configuration needs (e.g. throughout, authentication, format, retry strategy, or even error tolerance). For more information about Dapr's pub/sub see these [docs](https://github.com/dapr/docs/tree/master/concepts/publish-subscribe-messaging) + +This demo requires Dapr `v0.11` as well as go `1.14+` and docker-compose `v1.26+` + +> Note, the version of the demo with Event Hub binding has been moved to [this branch](https://github.com/mchmarny/dapr-demos/tree/fanout-eventhubs/fan-out) + +## App 2: Pub/Sub to Pub/Sub Publisher + +To run these demos you will need access to Redis and Kafka servers. For Redis, you can use the one installed during local Dapr setup. For Kafka, you can use the included Docker Compose file. First, navigate to the `queue-format-converter` and start Kafka: + +```shell +docker-compose -f ./config/kafka.yaml up -d +``` + +The result should look something like this: + +```shell +Creating network "config_default" with the default driver +Creating config_kafka_1 ... done +Creating config_zookeeper_1 ... done +``` + +Now, start `App 2` which will receive events from Redis, convert them to XML, and publish them onto the Kafka topic: + +```shell +dapr run \ + --app-id app2 \ + --app-port 60010 \ + --app-protocol grpc \ + --components-path ./config \ + go run main.go +``` + +> The source and targets Pub/Sub components are defined in the [./config](./queue-format-converter/config) directory in their respected files so the use code is free of SDK and libraries which allows for easy re-configuration at run-time. + +Leave the application running, we will come back to it after configuring `App 1` + +## App 1: Pub/Sub Event Producer + +To demo the above `App 2` we will need events. To produce events, in another terminal session navigate to `queue-event-producer` directory, start the `App 1`: + +```shell +dapr run \ + --app-id app1 \ + --app-port 60013 \ + --app-protocol grpc \ + --components-path ./config \ + go run main.go +``` + +The app will now publish one event every `3s`. To change the frequency just define the desired duration using `THREAD_PUB_FREQ` variable and restart the app. The results should look something like this: + +```shell +== APP == published: {"id":"df50a6c7-b5bb-45ce-b3a8-ad428bbbd5fe","temperature":60.46998219508215,"humidity":94.05150371362079,"time":1598960035} +``` + +Now in the `App 1`, the log output for each event should look something like this: + +```shell +== APP == Event - PubsubName:fanout-source-pubsub, Topic:events, ID:5ffa4502-8bf1-4bbf-927e-8b62e1949166 +== APP == Target (csv): "45ecf820-705b-47c2-a2e4-7dbb3eecb728",66.459360,43.777042,"2020-09-01T04:33:58-07:00" +``` + +## App 3: Pub/Sub to External REST Endpoint Publisher + +To add another publisher which converts the received events into JSON and publishes them to the component defined REST endpoint, first navigate to the `http-format-converter` directory and start `App 3`: + +```shell +dapr run \ + --app-id app3 \ + --app-port 60011 \ + --app-protocol grpc \ + --components-path ./config \ + go run main.go +``` + +> This app is configured with an HTTP binding that defines the target and verb of the invocation. This external configuration allows for easy change of the target binding to something like email using SendGrid or AWS S3 without changing the use-code. To learn more about Dapr bindings, see these [docs](https://github.com/dapr/docs/tree/master/concepts/bindings#supported-bindings-and-specs) + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-http-target-post-binding +spec: + type: bindings.http + metadata: + - name: url + value: https://postman-echo.com/post + - name: method + value: POST +``` + +If the `App 1` is still running, you should see entries in the log similar to this: + +```shell +== APP == Event - PubsubName:fanout-source-pubsub, Topic:events, ID:d5bbcc9e-f0d3-46df-8d8e-f7dadb3f304c +== APP == Target (json): {"temperature":20.326656,"humidity":36.093533,"time":1598961599,"id":"b2fa85cf-1be6-4489-9aec-613cf969675e"} +``` + +## App 4: Pub/Sub to another Dapr Service Publisher + +To add the final publisher, which will convert events into XML format and publish them to another Dapr service over gRPC, first navigate to the `grpc-echo-service` directory and start target service. For demo purposes, we will use the included echo service which simply returns whatever message it receives. + +```shell +dapr run \ + --app-id grpc-echo-service \ + --app-port 60015 \ + --app-protocol grpc \ + go run main.go +``` + +Then, in yet another terminal session, navigate to the `service-format-converter` directory and start the `App 4` that will invoke the `grpc-echo-service`: + +```shell +dapr run \ + --app-id app4 \ + --app-port 60012 \ + --app-protocol grpc \ + --components-path ./config \ + go run main.go +``` + +If everything went well, you should see something similar in the `App 4` logs: + +```shell +== APP == Target: &{Data:[60 84 111 112 105 99 69 118...] ContentType:application/xml} +== APP == Response: df450605-4927-407e-a2b2-61233939ee581.0com.dapr.event.sentapp1application/json{"time":1598962950,"id":"745e3ebe-990b-4292-9347-f84ff81f41e6","temperature":31.812637,"humidity":46.894296}eventsfanout-source-pubsub +``` + +> If you left all the apps running, you can go to each terminal session and see the different formats which are generated and published by each application. + + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/fan-out/grpc-echo-service/Dockerfile b/fan-out/grpc-echo-service/Dockerfile index f5904ac..a5c01c7 100644 --- a/fan-out/grpc-echo-service/Dockerfile +++ b/fan-out/grpc-echo-service/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/fan-out/grpc-echo-service/Makefile b/fan-out/grpc-echo-service/Makefile index fff5cd3..80bdff5 100644 --- a/fan-out/grpc-echo-service/Makefile +++ b/fan-out/grpc-echo-service/Makefile @@ -1,69 +1,69 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=grpc-echo-service -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: test -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60002 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - go run main.go - -.PHONY: invoke -invoke: ## Invokes service through Dapr API - curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - "http://localhost:3500/v1.0/invoke/$(SERVICE_NAME)/method/echo" - -.PHONY: image -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f deployment.yaml - kubectl rollout restart deployment/grpc-echo-service - kubectl rollout status deployment/grpc-echo-service - -.PHONY: call -call: ## Invokes service through Dapr API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/echo" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=grpc-echo-service +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: test +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60002 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + go run main.go + +.PHONY: invoke +invoke: ## Invokes service through Dapr API + curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + "http://localhost:3500/v1.0/invoke/$(SERVICE_NAME)/method/echo" + +.PHONY: image +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f deployment.yaml + kubectl rollout restart deployment/grpc-echo-service + kubectl rollout status deployment/grpc-echo-service + +.PHONY: call +call: ## Invokes service through Dapr API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/echo" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/fan-out/grpc-echo-service/README.md b/fan-out/grpc-echo-service/README.md index 1a31ee6..abea96d 100644 --- a/fan-out/grpc-echo-service/README.md +++ b/fan-out/grpc-echo-service/README.md @@ -1,76 +1,76 @@ -# grpc-service - -For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) - -## Run - -To run this demo in Dapr, run: - -```shell -dapr run \ - --app-id grpc-service-demo \ - --app-port 50001 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go -``` - -## Deploy - -Deploy and wait for the pod to be ready - -```shell -kubectl apply -f deployment.yaml -kubectl rollout status deployment/grpc-echo-service -``` - -If you have changed an existing component, make sure to reload the ingress and wait until the new version is ready - -```shell -kubectl rollout restart deployment/nginx-ingress-nginx-controller -kubectl rollout status deployment/nginx-ingress-nginx-controller -``` - -Follow logs - -```shell -kubectl logs -l app=grpc-echo-service -c service -f -``` - -In a separate terminal session export API token - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) -``` - -And invoke the service - -```shell -curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/invoke/grpc-echo-service/method/echo" -``` - -The response should include the sent message - -```json -{ - "message": "ping" -} -``` - -And the logs - -```shell -Invocation (ContentType:application/json, Verb:POST, QueryString:map[], Data:{ "message": "ping" }) -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# grpc-service + +For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) + +## Run + +To run this demo in Dapr, run: + +```shell +dapr run \ + --app-id grpc-service-demo \ + --app-port 50001 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go +``` + +## Deploy + +Deploy and wait for the pod to be ready + +```shell +kubectl apply -f deployment.yaml +kubectl rollout status deployment/grpc-echo-service +``` + +If you have changed an existing component, make sure to reload the ingress and wait until the new version is ready + +```shell +kubectl rollout restart deployment/nginx-ingress-nginx-controller +kubectl rollout status deployment/nginx-ingress-nginx-controller +``` + +Follow logs + +```shell +kubectl logs -l app=grpc-echo-service -c service -f +``` + +In a separate terminal session export API token + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) +``` + +And invoke the service + +```shell +curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/invoke/grpc-echo-service/method/echo" +``` + +The response should include the sent message + +```json +{ + "message": "ping" +} +``` + +And the logs + +```shell +Invocation (ContentType:application/json, Verb:POST, QueryString:map[], Data:{ "message": "ping" }) +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/fan-out/grpc-echo-service/deployment.yaml b/fan-out/grpc-echo-service/deployment.yaml index c379229..a15655a 100644 --- a/fan-out/grpc-echo-service/deployment.yaml +++ b/fan-out/grpc-echo-service/deployment.yaml @@ -1,35 +1,35 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc-echo-service - labels: - app: grpc-echo-service - demo: echo -spec: - replicas: 1 - selector: - matchLabels: - app: grpc-echo-service - template: - metadata: - labels: - app: grpc-echo-service - demo: echo - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "grpc-echo-service" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-echo-service:v0.11.1 - imagePullPolicy: Always - ports: - - containerPort: 60002 - env: - - name: ADDRESS +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-echo-service + labels: + app: grpc-echo-service + demo: echo +spec: + replicas: 1 + selector: + matchLabels: + app: grpc-echo-service + template: + metadata: + labels: + app: grpc-echo-service + demo: echo + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "grpc-echo-service" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-echo-service:v0.11.1 + imagePullPolicy: Always + ports: + - containerPort: 60002 + env: + - name: ADDRESS value: ":60002" \ No newline at end of file diff --git a/fan-out/grpc-echo-service/go.mod b/fan-out/grpc-echo-service/go.mod index ec7b873..bc247f2 100644 --- a/fan-out/grpc-echo-service/go.mod +++ b/fan-out/grpc-echo-service/go.mod @@ -1,5 +1,5 @@ -module github.com/mchmarny/dapr-demos/grpc-echo-service - -go 1.15 - -require github.com/dapr/go-sdk v0.11.0 +module github.com/mchmarny/dapr-demos/grpc-echo-service + +go 1.15 + +require github.com/dapr/go-sdk v0.11.0 diff --git a/fan-out/grpc-echo-service/go.sum b/fan-out/grpc-echo-service/go.sum index 6bf079a..77d8561 100644 --- a/fan-out/grpc-echo-service/go.sum +++ b/fan-out/grpc-echo-service/go.sum @@ -1,100 +1,100 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/fan-out/grpc-echo-service/main.go b/fan-out/grpc-echo-service/main.go index a6c510c..6e694b5 100644 --- a/fan-out/grpc-echo-service/main.go +++ b/fan-out/grpc-echo-service/main.go @@ -1,55 +1,55 @@ -package main - -import ( - "context" - "log" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" -) - -var ( - logger = log.New(os.Stdout, "", 0) - serviceAddress = getEnvVar("ADDRESS", ":60015") -) - -func main() { - // create serving server - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - // add handler to the service - s.AddServiceInvocationHandler("echo", echoHandler) - - // start the server to handle incoming events - log.Printf("starting server at %s...", serviceAddress) - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func echoHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - logger.Printf( - "Invocation (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", - in.ContentType, in.Verb, in.QueryString, string(in.Data), - ) - - // TODO: implement handling logic here - out = &common.Content{ - ContentType: in.ContentType, - Data: in.Data, - } - - return -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" +) + +var ( + logger = log.New(os.Stdout, "", 0) + serviceAddress = getEnvVar("ADDRESS", ":60015") +) + +func main() { + // create serving server + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + // add handler to the service + s.AddServiceInvocationHandler("echo", echoHandler) + + // start the server to handle incoming events + log.Printf("starting server at %s...", serviceAddress) + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func echoHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + logger.Printf( + "Invocation (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", + in.ContentType, in.Verb, in.QueryString, string(in.Data), + ) + + // TODO: implement handling logic here + out = &common.Content{ + ContentType: in.ContentType, + Data: in.Data, + } + + return +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/fan-out/http-format-converter/Dockerfile b/fan-out/http-format-converter/Dockerfile index f5904ac..a5c01c7 100644 --- a/fan-out/http-format-converter/Dockerfile +++ b/fan-out/http-format-converter/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/fan-out/http-format-converter/Makefile b/fan-out/http-format-converter/Makefile index dd49fbf..f7d503e 100644 --- a/fan-out/http-format-converter/Makefile +++ b/fan-out/http-format-converter/Makefile @@ -1,57 +1,57 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=http-format-converter -DOCKER_USERNAME ?=$(DOCKER_USER) -TARGET_FORMAT =xml - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: tidy -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) - TARGET_FORMAT=$(TARGET_FORMAT) dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60011 \ - --app-protocol grpc \ - --components-path ./config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/components.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/$(SERVICE_NAME) - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=http-format-converter +DOCKER_USERNAME ?=$(DOCKER_USER) +TARGET_FORMAT =xml + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: tidy +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) + TARGET_FORMAT=$(TARGET_FORMAT) dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60011 \ + --app-protocol grpc \ + --components-path ./config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/components.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/$(SERVICE_NAME) + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/fan-out/http-format-converter/config/source-pubsub.yaml b/fan-out/http-format-converter/config/source-pubsub.yaml index 4ec62e6..05f2845 100644 --- a/fan-out/http-format-converter/config/source-pubsub.yaml +++ b/fan-out/http-format-converter/config/source-pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-source-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-source-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/fan-out/http-format-converter/config/target-binding.yaml b/fan-out/http-format-converter/config/target-binding.yaml index f321ac7..db25318 100644 --- a/fan-out/http-format-converter/config/target-binding.yaml +++ b/fan-out/http-format-converter/config/target-binding.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-http-target-post-binding -spec: - type: bindings.http - metadata: - - name: url - value: https://postman-echo.com/post - - name: method +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-http-target-post-binding +spec: + type: bindings.http + metadata: + - name: url + value: https://postman-echo.com/post + - name: method value: POST \ No newline at end of file diff --git a/fan-out/http-format-converter/go.mod b/fan-out/http-format-converter/go.mod index 1d2eef3..057e7d3 100644 --- a/fan-out/http-format-converter/go.mod +++ b/fan-out/http-format-converter/go.mod @@ -1,8 +1,8 @@ -module github.com/mchmarny/dapr-demos/fan-out/http-format-converter - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/fan-out/http-format-converter + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 +) diff --git a/fan-out/http-format-converter/go.sum b/fan-out/http-format-converter/go.sum index 6bf079a..77d8561 100644 --- a/fan-out/http-format-converter/go.sum +++ b/fan-out/http-format-converter/go.sum @@ -1,100 +1,100 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/fan-out/http-format-converter/k8s/components.yaml b/fan-out/http-format-converter/k8s/components.yaml index 8d7230b..af9aef5 100644 --- a/fan-out/http-format-converter/k8s/components.yaml +++ b/fan-out/http-format-converter/k8s/components.yaml @@ -1,17 +1,17 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: grpc-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password - - name: allowedTopics - value: "messages" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: grpc-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password + - name: allowedTopics + value: "messages" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/http-format-converter/k8s/deployment.yaml b/fan-out/http-format-converter/k8s/deployment.yaml index bab57c5..df016c6 100644 --- a/fan-out/http-format-converter/k8s/deployment.yaml +++ b/fan-out/http-format-converter/k8s/deployment.yaml @@ -1,37 +1,37 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc-event-subscriber - labels: - app: grpc-event-subscriber - demo: grpc-event -spec: - selector: - matchLabels: - app: grpc-event-subscriber - template: - metadata: - labels: - app: grpc-event-subscriber - demo: grpc-event - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "grpc-event-subscriber" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-event-subscriber:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: ADDRESS - value: ":60002" - - name: PUBSUB_NAME - value: "grpc-events" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-event-subscriber + labels: + app: grpc-event-subscriber + demo: grpc-event +spec: + selector: + matchLabels: + app: grpc-event-subscriber + template: + metadata: + labels: + app: grpc-event-subscriber + demo: grpc-event + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "grpc-event-subscriber" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-event-subscriber:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: ADDRESS + value: ":60002" + - name: PUBSUB_NAME + value: "grpc-events" + - name: TOPIC_NAME + value: "messages" diff --git a/fan-out/http-format-converter/main.go b/fan-out/http-format-converter/main.go index 1feed6d..3d16167 100644 --- a/fan-out/http-format-converter/main.go +++ b/fan-out/http-format-converter/main.go @@ -1,119 +1,119 @@ -package main - -import ( - "context" - "encoding/json" - "encoding/xml" - "fmt" - "log" - "os" - "strings" - "time" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/pkg/errors" -) - -var ( - logger = log.New(os.Stdout, "", 0) - client dapr.Client - - serviceAddress = getEnvVar("ADDRESS", ":60011") - - sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") - sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "events") - - targetBindingName = getEnvVar("TARGET_BINDING", "fanout-http-target-post-binding") - targetFormat = getEnvVar("TARGET_FORMAT", "json") -) - -func main() { - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - c, err := dapr.NewClient() - if err != nil { - log.Fatalf("failed to create Dapr client: %v", err) - } - client = c - defer client.Close() - - // add handler to the service - sub := &common.Subscription{PubsubName: sourcePubSubName, Topic: sourceTopicName} - s.AddTopicEventHandler(sub, eventHandler) - - // start the server to handle incoming events - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -// SourceEvent represents the input event -type SourceEvent struct { - ID string `json:"id"` - Temperature float64 `json:"temperature"` - Humidity float64 `json:"humidity"` - Time int64 `json:"time"` -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) - - d, ok := e.Data.([]byte) - if !ok { - return false, errors.Errorf("invalid event data type: %T", e.Data) - } - - var se SourceEvent - if err := json.Unmarshal(d, &se); err != nil { - return false, errors.Errorf("error parsing input content: %v", err) - } - - var ( - me error - b []byte - ) - - switch strings.ToLower(targetFormat) { - case "json": - b = d - case "xml": - if b, me = xml.Marshal(&e); me != nil { - return false, errors.Errorf("error while converting content: %v", me) - } - case "csv": - b = []byte(fmt.Sprintf(`"%s",%f,%f,"%s"`, - se.ID, se.Temperature, se.Humidity, time.Unix(se.Time, 0).Format(time.RFC3339))) - default: - return false, errors.Errorf("invalid target format: %s", targetFormat) - } - logger.Printf("Target (%s): %s", targetFormat, b) - - content := &dapr.BindingInvocation{ - Data: b, - Metadata: map[string]string{ - "record-id": e.ID, - "conversion-time": time.Now().UTC().Format(time.RFC3339), - }, - Name: targetBindingName, - Operation: "create", - } - - if err := client.InvokeOutputBinding(ctx, content); err != nil { - return true, errors.Wrap(err, "error invoking target binding") - } - - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "encoding/xml" + "fmt" + "log" + "os" + "strings" + "time" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/pkg/errors" +) + +var ( + logger = log.New(os.Stdout, "", 0) + client dapr.Client + + serviceAddress = getEnvVar("ADDRESS", ":60011") + + sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") + sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "events") + + targetBindingName = getEnvVar("TARGET_BINDING", "fanout-http-target-post-binding") + targetFormat = getEnvVar("TARGET_FORMAT", "json") +) + +func main() { + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + c, err := dapr.NewClient() + if err != nil { + log.Fatalf("failed to create Dapr client: %v", err) + } + client = c + defer client.Close() + + // add handler to the service + sub := &common.Subscription{PubsubName: sourcePubSubName, Topic: sourceTopicName} + s.AddTopicEventHandler(sub, eventHandler) + + // start the server to handle incoming events + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +// SourceEvent represents the input event +type SourceEvent struct { + ID string `json:"id"` + Temperature float64 `json:"temperature"` + Humidity float64 `json:"humidity"` + Time int64 `json:"time"` +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) + + d, ok := e.Data.([]byte) + if !ok { + return false, errors.Errorf("invalid event data type: %T", e.Data) + } + + var se SourceEvent + if err := json.Unmarshal(d, &se); err != nil { + return false, errors.Errorf("error parsing input content: %v", err) + } + + var ( + me error + b []byte + ) + + switch strings.ToLower(targetFormat) { + case "json": + b = d + case "xml": + if b, me = xml.Marshal(&e); me != nil { + return false, errors.Errorf("error while converting content: %v", me) + } + case "csv": + b = []byte(fmt.Sprintf(`"%s",%f,%f,"%s"`, + se.ID, se.Temperature, se.Humidity, time.Unix(se.Time, 0).Format(time.RFC3339))) + default: + return false, errors.Errorf("invalid target format: %s", targetFormat) + } + logger.Printf("Target (%s): %s", targetFormat, b) + + content := &dapr.BindingInvocation{ + Data: b, + Metadata: map[string]string{ + "record-id": e.ID, + "conversion-time": time.Now().UTC().Format(time.RFC3339), + }, + Name: targetBindingName, + Operation: "create", + } + + if err := client.InvokeOutputBinding(ctx, content); err != nil { + return true, errors.Wrap(err, "error invoking target binding") + } + + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/fan-out/queue-event-consumer/Dockerfile b/fan-out/queue-event-consumer/Dockerfile index f5904ac..a5c01c7 100644 --- a/fan-out/queue-event-consumer/Dockerfile +++ b/fan-out/queue-event-consumer/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/fan-out/queue-event-consumer/Makefile b/fan-out/queue-event-consumer/Makefile index 8b53984..b068977 100644 --- a/fan-out/queue-event-consumer/Makefile +++ b/fan-out/queue-event-consumer/Makefile @@ -1,57 +1,57 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=queue-format-consumer -DOCKER_USERNAME ?=$(DOCKER_USER) -TARGET_FORMAT =xml - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: tidy -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60030 \ - --app-protocol grpc \ - --components-path ./config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/components.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/$(SERVICE_NAME) - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=queue-format-consumer +DOCKER_USERNAME ?=$(DOCKER_USER) +TARGET_FORMAT =xml + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: tidy +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60030 \ + --app-protocol grpc \ + --components-path ./config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/components.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/$(SERVICE_NAME) + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/fan-out/queue-event-consumer/config/source-pabusb.yaml b/fan-out/queue-event-consumer/config/source-pabusb.yaml index 4ec62e6..05f2845 100644 --- a/fan-out/queue-event-consumer/config/source-pabusb.yaml +++ b/fan-out/queue-event-consumer/config/source-pabusb.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-source-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-source-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/fan-out/queue-event-consumer/go.mod b/fan-out/queue-event-consumer/go.mod index 5184962..385dbda 100644 --- a/fan-out/queue-event-consumer/go.mod +++ b/fan-out/queue-event-consumer/go.mod @@ -1,5 +1,5 @@ -module github.com/mchmarny/dapr-demos/fan-out/queue-format-consumer - -go 1.15 - -require github.com/dapr/go-sdk v0.11.0 +module github.com/mchmarny/dapr-demos/fan-out/queue-format-consumer + +go 1.15 + +require github.com/dapr/go-sdk v0.11.0 diff --git a/fan-out/queue-event-consumer/go.sum b/fan-out/queue-event-consumer/go.sum index 6bf079a..77d8561 100644 --- a/fan-out/queue-event-consumer/go.sum +++ b/fan-out/queue-event-consumer/go.sum @@ -1,100 +1,100 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/fan-out/queue-event-consumer/k8s/deployment.yaml b/fan-out/queue-event-consumer/k8s/deployment.yaml index bab57c5..df016c6 100644 --- a/fan-out/queue-event-consumer/k8s/deployment.yaml +++ b/fan-out/queue-event-consumer/k8s/deployment.yaml @@ -1,37 +1,37 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc-event-subscriber - labels: - app: grpc-event-subscriber - demo: grpc-event -spec: - selector: - matchLabels: - app: grpc-event-subscriber - template: - metadata: - labels: - app: grpc-event-subscriber - demo: grpc-event - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "grpc-event-subscriber" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-event-subscriber:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: ADDRESS - value: ":60002" - - name: PUBSUB_NAME - value: "grpc-events" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-event-subscriber + labels: + app: grpc-event-subscriber + demo: grpc-event +spec: + selector: + matchLabels: + app: grpc-event-subscriber + template: + metadata: + labels: + app: grpc-event-subscriber + demo: grpc-event + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "grpc-event-subscriber" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-event-subscriber:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: ADDRESS + value: ":60002" + - name: PUBSUB_NAME + value: "grpc-events" + - name: TOPIC_NAME + value: "messages" diff --git a/fan-out/queue-event-consumer/k8s/target-kafka.yaml b/fan-out/queue-event-consumer/k8s/target-kafka.yaml index eec8a39..94538f2 100644 --- a/fan-out/queue-event-consumer/k8s/target-kafka.yaml +++ b/fan-out/queue-event-consumer/k8s/target-kafka.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-queue-kafka-target -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: "localhost:9092" - - name: authRequired - value: "false" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-queue-kafka-target +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: "localhost:9092" + - name: authRequired + value: "false" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/queue-event-consumer/k8s/target-redis.yaml b/fan-out/queue-event-consumer/k8s/target-redis.yaml index eec8a39..94538f2 100644 --- a/fan-out/queue-event-consumer/k8s/target-redis.yaml +++ b/fan-out/queue-event-consumer/k8s/target-redis.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-queue-kafka-target -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: "localhost:9092" - - name: authRequired - value: "false" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-queue-kafka-target +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: "localhost:9092" + - name: authRequired + value: "false" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/queue-event-consumer/main.go b/fan-out/queue-event-consumer/main.go index 8e9332d..b6f0fc2 100644 --- a/fan-out/queue-event-consumer/main.go +++ b/fan-out/queue-event-consumer/main.go @@ -1,50 +1,50 @@ -package main - -import ( - "context" - "log" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" -) - -var ( - logger = log.New(os.Stdout, "", 0) - serviceAddress = getEnvVar("ADDRESS", ":60030") - sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") - sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "test-topic") -) - -func main() { - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - // add handler to the service - sub := &common.Subscription{ - PubsubName: sourcePubSubName, - Topic: sourceTopicName, - } - s.AddTopicEventHandler(sub, eventHandler) - - // start the server to handle incoming events - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" +) + +var ( + logger = log.New(os.Stdout, "", 0) + serviceAddress = getEnvVar("ADDRESS", ":60030") + sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") + sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "test-topic") +) + +func main() { + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + // add handler to the service + sub := &common.Subscription{ + PubsubName: sourcePubSubName, + Topic: sourceTopicName, + } + s.AddTopicEventHandler(sub, eventHandler) + + // start the server to handle incoming events + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/fan-out/queue-event-producer/Dockerfile b/fan-out/queue-event-producer/Dockerfile index f5904ac..a5c01c7 100644 --- a/fan-out/queue-event-producer/Dockerfile +++ b/fan-out/queue-event-producer/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/fan-out/queue-event-producer/Makefile b/fan-out/queue-event-producer/Makefile index 7e53c48..a5d7a28 100644 --- a/fan-out/queue-event-producer/Makefile +++ b/fan-out/queue-event-producer/Makefile @@ -1,56 +1,56 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=queue-event-producer -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: tidy -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60013 \ - --app-protocol grpc \ - --components-path ./config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/components.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/$(SERVICE_NAME) - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=queue-event-producer +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: tidy +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60013 \ + --app-protocol grpc \ + --components-path ./config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/components.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/$(SERVICE_NAME) + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/fan-out/queue-event-producer/config/target-pabusb.yaml b/fan-out/queue-event-producer/config/target-pabusb.yaml index 4ec62e6..05f2845 100644 --- a/fan-out/queue-event-producer/config/target-pabusb.yaml +++ b/fan-out/queue-event-producer/config/target-pabusb.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-source-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-source-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/fan-out/queue-event-producer/go.mod b/fan-out/queue-event-producer/go.mod index 48251ad..7dff658 100644 --- a/fan-out/queue-event-producer/go.mod +++ b/fan-out/queue-event-producer/go.mod @@ -1,9 +1,9 @@ -module github.com/mchmarny/dapr-demos/fan-out/queue-event-producer - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/google/uuid v1.1.2 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/fan-out/queue-event-producer + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/google/uuid v1.1.2 + github.com/pkg/errors v0.9.1 +) diff --git a/fan-out/queue-event-producer/go.sum b/fan-out/queue-event-producer/go.sum index 5435e88..b8242ca 100644 --- a/fan-out/queue-event-producer/go.sum +++ b/fan-out/queue-event-producer/go.sum @@ -1,102 +1,102 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/fan-out/queue-event-producer/k8s/deployment.yaml b/fan-out/queue-event-producer/k8s/deployment.yaml index bab57c5..df016c6 100644 --- a/fan-out/queue-event-producer/k8s/deployment.yaml +++ b/fan-out/queue-event-producer/k8s/deployment.yaml @@ -1,37 +1,37 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc-event-subscriber - labels: - app: grpc-event-subscriber - demo: grpc-event -spec: - selector: - matchLabels: - app: grpc-event-subscriber - template: - metadata: - labels: - app: grpc-event-subscriber - demo: grpc-event - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "grpc-event-subscriber" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-event-subscriber:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: ADDRESS - value: ":60002" - - name: PUBSUB_NAME - value: "grpc-events" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-event-subscriber + labels: + app: grpc-event-subscriber + demo: grpc-event +spec: + selector: + matchLabels: + app: grpc-event-subscriber + template: + metadata: + labels: + app: grpc-event-subscriber + demo: grpc-event + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "grpc-event-subscriber" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-event-subscriber:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: ADDRESS + value: ":60002" + - name: PUBSUB_NAME + value: "grpc-events" + - name: TOPIC_NAME + value: "messages" diff --git a/fan-out/queue-event-producer/k8s/target-kafka.yaml b/fan-out/queue-event-producer/k8s/target-kafka.yaml index eec8a39..94538f2 100644 --- a/fan-out/queue-event-producer/k8s/target-kafka.yaml +++ b/fan-out/queue-event-producer/k8s/target-kafka.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-queue-kafka-target -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: "localhost:9092" - - name: authRequired - value: "false" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-queue-kafka-target +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: "localhost:9092" + - name: authRequired + value: "false" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/queue-event-producer/k8s/target-redis.yaml b/fan-out/queue-event-producer/k8s/target-redis.yaml index eec8a39..94538f2 100644 --- a/fan-out/queue-event-producer/k8s/target-redis.yaml +++ b/fan-out/queue-event-producer/k8s/target-redis.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-queue-kafka-target -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: "localhost:9092" - - name: authRequired - value: "false" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-queue-kafka-target +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: "localhost:9092" + - name: authRequired + value: "false" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/queue-event-producer/main.go b/fan-out/queue-event-producer/main.go index e121701..a8c9468 100644 --- a/fan-out/queue-event-producer/main.go +++ b/fan-out/queue-event-producer/main.go @@ -1,108 +1,108 @@ -package main - -import ( - "context" - "encoding/json" - "log" - "math/rand" - "os" - "strings" - "time" - - dapr "github.com/dapr/go-sdk/client" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/google/uuid" - "github.com/pkg/errors" -) - -var ( - logger = log.New(os.Stdout, "", 0) - - serviceAddress = getEnvVar("ADDRESS", ":60013") - - targetPubSubName = getEnvVar("TARGET_PUBSUB_NAME", "fanout-source-pubsub") - targetTopicName = getEnvVar("TARGET_TOPIC_NAME", "events") - - threadFreq = getEnvVar("THREAD_PUB_FREQ", "3s") -) - -func main() { - ctx := context.Background() - - tf, err := time.ParseDuration(threadFreq) - if err != nil { - logger.Fatalf("invalid thread frequency, expected duration: %s - %v", threadFreq, err) - } - logger.Printf("thread frequency: %s", tf) - - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - logger.Fatalf("failed to start the server: %v", err) - } - defer s.Stop() - - // dapr client - c, err := dapr.NewClient() - if err != nil { - logger.Fatalf("failed to create Dapr client: %v", err) - } - defer c.Close() - - // timer - timer := time.NewTicker(tf) - defer timer.Stop() - - // produce - go func() { - if err := produce(ctx, c, timer); err != nil { - logger.Fatalf("error: %v", err) - } - }() - - // start the server to handle incoming events - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func produce(ctx context.Context, c dapr.Client, t *time.Ticker) error { - for { - select { - case <-t.C: - b, err := json.Marshal(getRoomReading()) - if err != nil { - return errors.Wrap(err, "error serializing reading") - } - if err := c.PublishEvent(ctx, targetPubSubName, targetTopicName, b); err != nil { - return errors.Wrap(err, "error publishing content") - } - logger.Printf("published: %s", b) - } - } -} - -type roomReading struct { - ID string `json:"id"` - Temperature float64 `json:"temperature"` - Humidity float64 `json:"humidity"` - Time int64 `json:"time"` -} - -func getRoomReading() interface{} { - min := 0.01 - max := 100.00 - return &roomReading{ - ID: uuid.New().String(), - Temperature: min + rand.Float64()*(max-min), - Humidity: min + rand.Float64()*(max-min), - Time: time.Now().UTC().Unix(), - } -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "log" + "math/rand" + "os" + "strings" + "time" + + dapr "github.com/dapr/go-sdk/client" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/google/uuid" + "github.com/pkg/errors" +) + +var ( + logger = log.New(os.Stdout, "", 0) + + serviceAddress = getEnvVar("ADDRESS", ":60013") + + targetPubSubName = getEnvVar("TARGET_PUBSUB_NAME", "fanout-source-pubsub") + targetTopicName = getEnvVar("TARGET_TOPIC_NAME", "events") + + threadFreq = getEnvVar("THREAD_PUB_FREQ", "3s") +) + +func main() { + ctx := context.Background() + + tf, err := time.ParseDuration(threadFreq) + if err != nil { + logger.Fatalf("invalid thread frequency, expected duration: %s - %v", threadFreq, err) + } + logger.Printf("thread frequency: %s", tf) + + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + logger.Fatalf("failed to start the server: %v", err) + } + defer s.Stop() + + // dapr client + c, err := dapr.NewClient() + if err != nil { + logger.Fatalf("failed to create Dapr client: %v", err) + } + defer c.Close() + + // timer + timer := time.NewTicker(tf) + defer timer.Stop() + + // produce + go func() { + if err := produce(ctx, c, timer); err != nil { + logger.Fatalf("error: %v", err) + } + }() + + // start the server to handle incoming events + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func produce(ctx context.Context, c dapr.Client, t *time.Ticker) error { + for { + select { + case <-t.C: + b, err := json.Marshal(getRoomReading()) + if err != nil { + return errors.Wrap(err, "error serializing reading") + } + if err := c.PublishEvent(ctx, targetPubSubName, targetTopicName, b); err != nil { + return errors.Wrap(err, "error publishing content") + } + logger.Printf("published: %s", b) + } + } +} + +type roomReading struct { + ID string `json:"id"` + Temperature float64 `json:"temperature"` + Humidity float64 `json:"humidity"` + Time int64 `json:"time"` +} + +func getRoomReading() interface{} { + min := 0.01 + max := 100.00 + return &roomReading{ + ID: uuid.New().String(), + Temperature: min + rand.Float64()*(max-min), + Humidity: min + rand.Float64()*(max-min), + Time: time.Now().UTC().Unix(), + } +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/fan-out/queue-format-converter/Dockerfile b/fan-out/queue-format-converter/Dockerfile index f5904ac..a5c01c7 100644 --- a/fan-out/queue-format-converter/Dockerfile +++ b/fan-out/queue-format-converter/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/fan-out/queue-format-converter/Makefile b/fan-out/queue-format-converter/Makefile index 1590711..13e4c5f 100644 --- a/fan-out/queue-format-converter/Makefile +++ b/fan-out/queue-format-converter/Makefile @@ -1,57 +1,57 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=queue-format-converter -DOCKER_USERNAME ?=$(DOCKER_USER) -TARGET_FORMAT =xml - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: tidy -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) - TARGET_TOPIC_FORMAT=$(TARGET_FORMAT) dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60010 \ - --app-protocol grpc \ - --components-path ./config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/components.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/$(SERVICE_NAME) - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=queue-format-converter +DOCKER_USERNAME ?=$(DOCKER_USER) +TARGET_FORMAT =xml + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: tidy +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) + TARGET_TOPIC_FORMAT=$(TARGET_FORMAT) dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60010 \ + --app-protocol grpc \ + --components-path ./config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/components.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/$(SERVICE_NAME) + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/fan-out/queue-format-converter/config/kafka.yaml b/fan-out/queue-format-converter/config/kafka.yaml index 5ea3383..2bc5e20 100644 --- a/fan-out/queue-format-converter/config/kafka.yaml +++ b/fan-out/queue-format-converter/config/kafka.yaml @@ -1,14 +1,14 @@ -version: '2' -services: - zookeeper: - image: wurstmeister/zookeeper:latest - ports: - - "2181:2181" - kafka: - image: wurstmeister/kafka:latest - ports: - - "9092:9092" - environment: - KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1 - KAFKA_CREATE_TOPICS: "events:1:1" +version: '2' +services: + zookeeper: + image: wurstmeister/zookeeper:latest + ports: + - "2181:2181" + kafka: + image: wurstmeister/kafka:latest + ports: + - "9092:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1 + KAFKA_CREATE_TOPICS: "events:1:1" KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 \ No newline at end of file diff --git a/fan-out/queue-format-converter/config/source-pabusb.yaml b/fan-out/queue-format-converter/config/source-pabusb.yaml index 4ec62e6..05f2845 100644 --- a/fan-out/queue-format-converter/config/source-pabusb.yaml +++ b/fan-out/queue-format-converter/config/source-pabusb.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-source-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-source-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/fan-out/queue-format-converter/config/target-pubsub.yaml b/fan-out/queue-format-converter/config/target-pubsub.yaml index 60149bc..13e0373 100644 --- a/fan-out/queue-format-converter/config/target-pubsub.yaml +++ b/fan-out/queue-format-converter/config/target-pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-target-pubsub -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: "localhost:9092" - - name: authRequired +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-target-pubsub +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: "localhost:9092" + - name: authRequired value: "false" \ No newline at end of file diff --git a/fan-out/queue-format-converter/go.mod b/fan-out/queue-format-converter/go.mod index e62c68b..7f8082a 100644 --- a/fan-out/queue-format-converter/go.mod +++ b/fan-out/queue-format-converter/go.mod @@ -1,8 +1,8 @@ -module github.com/mchmarny/dapr-demos/fan-out/queue-format-converter - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/fan-out/queue-format-converter + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 +) diff --git a/fan-out/queue-format-converter/go.sum b/fan-out/queue-format-converter/go.sum index 6bf079a..77d8561 100644 --- a/fan-out/queue-format-converter/go.sum +++ b/fan-out/queue-format-converter/go.sum @@ -1,100 +1,100 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/fan-out/queue-format-converter/k8s/deployment.yaml b/fan-out/queue-format-converter/k8s/deployment.yaml index bab57c5..df016c6 100644 --- a/fan-out/queue-format-converter/k8s/deployment.yaml +++ b/fan-out/queue-format-converter/k8s/deployment.yaml @@ -1,37 +1,37 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc-event-subscriber - labels: - app: grpc-event-subscriber - demo: grpc-event -spec: - selector: - matchLabels: - app: grpc-event-subscriber - template: - metadata: - labels: - app: grpc-event-subscriber - demo: grpc-event - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "grpc-event-subscriber" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-event-subscriber:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: ADDRESS - value: ":60002" - - name: PUBSUB_NAME - value: "grpc-events" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-event-subscriber + labels: + app: grpc-event-subscriber + demo: grpc-event +spec: + selector: + matchLabels: + app: grpc-event-subscriber + template: + metadata: + labels: + app: grpc-event-subscriber + demo: grpc-event + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "grpc-event-subscriber" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-event-subscriber:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: ADDRESS + value: ":60002" + - name: PUBSUB_NAME + value: "grpc-events" + - name: TOPIC_NAME + value: "messages" diff --git a/fan-out/queue-format-converter/k8s/target-kafka.yaml b/fan-out/queue-format-converter/k8s/target-kafka.yaml index eec8a39..94538f2 100644 --- a/fan-out/queue-format-converter/k8s/target-kafka.yaml +++ b/fan-out/queue-format-converter/k8s/target-kafka.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-queue-kafka-target -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: "localhost:9092" - - name: authRequired - value: "false" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-queue-kafka-target +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: "localhost:9092" + - name: authRequired + value: "false" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/queue-format-converter/k8s/target-redis.yaml b/fan-out/queue-format-converter/k8s/target-redis.yaml index eec8a39..94538f2 100644 --- a/fan-out/queue-format-converter/k8s/target-redis.yaml +++ b/fan-out/queue-format-converter/k8s/target-redis.yaml @@ -1,13 +1,13 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-queue-kafka-target -spec: - type: pubsub.kafka - metadata: - - name: brokers - value: "localhost:9092" - - name: authRequired - value: "false" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-queue-kafka-target +spec: + type: pubsub.kafka + metadata: + - name: brokers + value: "localhost:9092" + - name: authRequired + value: "false" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/queue-format-converter/main.go b/fan-out/queue-format-converter/main.go index c52faa5..c47644f 100644 --- a/fan-out/queue-format-converter/main.go +++ b/fan-out/queue-format-converter/main.go @@ -1,110 +1,110 @@ -package main - -import ( - "context" - "encoding/json" - "encoding/xml" - "fmt" - "log" - "os" - "strings" - "time" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/pkg/errors" -) - -var ( - logger = log.New(os.Stdout, "", 0) - client dapr.Client - - serviceAddress = getEnvVar("ADDRESS", ":60010") - - sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") - sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "events") - - targetPubSubName = getEnvVar("TARGET_PUBSUB_NAME", "fanout-target-pubsub") - targetTopicName = getEnvVar("TARGET_TOPIC_NAME", "events") - targetTopicFormat = getEnvVar("TARGET_TOPIC_FORMAT", "csv") -) - -func main() { - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - c, err := dapr.NewClient() - if err != nil { - log.Fatalf("failed to create Dapr client: %v", err) - } - client = c - defer client.Close() - - // add handler to the service - sub := &common.Subscription{PubsubName: sourcePubSubName, Topic: sourceTopicName} - s.AddTopicEventHandler(sub, eventHandler) - - // start the server to handle incoming events - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -// SourceEvent represents the input event -type SourceEvent struct { - ID string `json:"id"` - Temperature float64 `json:"temperature"` - Humidity float64 `json:"humidity"` - Time int64 `json:"time"` -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) - - d, ok := e.Data.([]byte) - if !ok { - return false, errors.Errorf("invalid event data type: %T", e.Data) - } - - var se SourceEvent - if err := json.Unmarshal(d, &se); err != nil { - return false, errors.Errorf("error parsing input content: %v", err) - } - - var ( - me error - b []byte - ) - - switch strings.ToLower(targetTopicFormat) { - case "json": - b = d - case "xml": - if b, me = xml.Marshal(&e); me != nil { - return false, errors.Errorf("error while converting content: %v", me) - } - case "csv": - b = []byte(fmt.Sprintf(`"%s",%f,%f,"%s"`, - se.ID, se.Temperature, se.Humidity, time.Unix(se.Time, 0).Format(time.RFC3339))) - default: - return false, errors.Errorf("invalid target format: %s", targetTopicFormat) - } - logger.Printf("Target (%s): %s", targetTopicFormat, b) - - if err := client.PublishEvent(ctx, targetPubSubName, targetTopicName, b); err != nil { - return true, errors.Wrap(err, "error publishing converted content") - } - - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "encoding/xml" + "fmt" + "log" + "os" + "strings" + "time" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/pkg/errors" +) + +var ( + logger = log.New(os.Stdout, "", 0) + client dapr.Client + + serviceAddress = getEnvVar("ADDRESS", ":60010") + + sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") + sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "events") + + targetPubSubName = getEnvVar("TARGET_PUBSUB_NAME", "fanout-target-pubsub") + targetTopicName = getEnvVar("TARGET_TOPIC_NAME", "events") + targetTopicFormat = getEnvVar("TARGET_TOPIC_FORMAT", "csv") +) + +func main() { + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + c, err := dapr.NewClient() + if err != nil { + log.Fatalf("failed to create Dapr client: %v", err) + } + client = c + defer client.Close() + + // add handler to the service + sub := &common.Subscription{PubsubName: sourcePubSubName, Topic: sourceTopicName} + s.AddTopicEventHandler(sub, eventHandler) + + // start the server to handle incoming events + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +// SourceEvent represents the input event +type SourceEvent struct { + ID string `json:"id"` + Temperature float64 `json:"temperature"` + Humidity float64 `json:"humidity"` + Time int64 `json:"time"` +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) + + d, ok := e.Data.([]byte) + if !ok { + return false, errors.Errorf("invalid event data type: %T", e.Data) + } + + var se SourceEvent + if err := json.Unmarshal(d, &se); err != nil { + return false, errors.Errorf("error parsing input content: %v", err) + } + + var ( + me error + b []byte + ) + + switch strings.ToLower(targetTopicFormat) { + case "json": + b = d + case "xml": + if b, me = xml.Marshal(&e); me != nil { + return false, errors.Errorf("error while converting content: %v", me) + } + case "csv": + b = []byte(fmt.Sprintf(`"%s",%f,%f,"%s"`, + se.ID, se.Temperature, se.Humidity, time.Unix(se.Time, 0).Format(time.RFC3339))) + default: + return false, errors.Errorf("invalid target format: %s", targetTopicFormat) + } + logger.Printf("Target (%s): %s", targetTopicFormat, b) + + if err := client.PublishEvent(ctx, targetPubSubName, targetTopicName, b); err != nil { + return true, errors.Wrap(err, "error publishing converted content") + } + + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/fan-out/service-format-converter/Dockerfile b/fan-out/service-format-converter/Dockerfile index f5904ac..a5c01c7 100644 --- a/fan-out/service-format-converter/Dockerfile +++ b/fan-out/service-format-converter/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/fan-out/service-format-converter/Makefile b/fan-out/service-format-converter/Makefile index 5d79714..e4323bc 100644 --- a/fan-out/service-format-converter/Makefile +++ b/fan-out/service-format-converter/Makefile @@ -1,62 +1,62 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=service-format-converter -DOCKER_USERNAME ?=$(DOCKER_USER) -TARGET_FORMAT =xml -TARGET_SERVICE =grpc-echo-service -TARGET_METHOD =echo - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: tidy -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) - TARGET_FORMAT=$(TARGET_FORMAT) \ - TARGET_SERVICE=$(TARGET_SERVICE) \ - TARGET_METHOD=$(TARGET_METHOD) \ - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60012 \ - --app-protocol grpc \ - --components-path ./config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/components.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/$(SERVICE_NAME) - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=service-format-converter +DOCKER_USERNAME ?=$(DOCKER_USER) +TARGET_FORMAT =xml +TARGET_SERVICE =grpc-echo-service +TARGET_METHOD =echo + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: tidy +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr (make run TARGET_FORMAT=csv) + TARGET_FORMAT=$(TARGET_FORMAT) \ + TARGET_SERVICE=$(TARGET_SERVICE) \ + TARGET_METHOD=$(TARGET_METHOD) \ + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60012 \ + --app-protocol grpc \ + --components-path ./config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/components.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/$(SERVICE_NAME) + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/fan-out/service-format-converter/config/source-pubsub.yaml b/fan-out/service-format-converter/config/source-pubsub.yaml index 4ec62e6..05f2845 100644 --- a/fan-out/service-format-converter/config/source-pubsub.yaml +++ b/fan-out/service-format-converter/config/source-pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: fanout-source-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: fanout-source-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/fan-out/service-format-converter/go.mod b/fan-out/service-format-converter/go.mod index 6cd8b86..53a79c8 100644 --- a/fan-out/service-format-converter/go.mod +++ b/fan-out/service-format-converter/go.mod @@ -1,8 +1,8 @@ -module github.com/mchmarny/dapr-demos/fan-out/service-format-converter - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/fan-out/service-format-converter + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 +) diff --git a/fan-out/service-format-converter/go.sum b/fan-out/service-format-converter/go.sum index 6bf079a..77d8561 100644 --- a/fan-out/service-format-converter/go.sum +++ b/fan-out/service-format-converter/go.sum @@ -1,100 +1,100 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/fan-out/service-format-converter/k8s/components.yaml b/fan-out/service-format-converter/k8s/components.yaml index 8d7230b..af9aef5 100644 --- a/fan-out/service-format-converter/k8s/components.yaml +++ b/fan-out/service-format-converter/k8s/components.yaml @@ -1,17 +1,17 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: grpc-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password - - name: allowedTopics - value: "messages" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: grpc-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password + - name: allowedTopics + value: "messages" +scopes: - xml-converter \ No newline at end of file diff --git a/fan-out/service-format-converter/k8s/deployment.yaml b/fan-out/service-format-converter/k8s/deployment.yaml index bab57c5..df016c6 100644 --- a/fan-out/service-format-converter/k8s/deployment.yaml +++ b/fan-out/service-format-converter/k8s/deployment.yaml @@ -1,37 +1,37 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc-event-subscriber - labels: - app: grpc-event-subscriber - demo: grpc-event -spec: - selector: - matchLabels: - app: grpc-event-subscriber - template: - metadata: - labels: - app: grpc-event-subscriber - demo: grpc-event - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "grpc-event-subscriber" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-event-subscriber:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: ADDRESS - value: ":60002" - - name: PUBSUB_NAME - value: "grpc-events" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-event-subscriber + labels: + app: grpc-event-subscriber + demo: grpc-event +spec: + selector: + matchLabels: + app: grpc-event-subscriber + template: + metadata: + labels: + app: grpc-event-subscriber + demo: grpc-event + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "grpc-event-subscriber" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-event-subscriber:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: ADDRESS + value: ":60002" + - name: PUBSUB_NAME + value: "grpc-events" + - name: TOPIC_NAME + value: "messages" diff --git a/fan-out/service-format-converter/main.go b/fan-out/service-format-converter/main.go index a1ffef4..4e50aa6 100644 --- a/fan-out/service-format-converter/main.go +++ b/fan-out/service-format-converter/main.go @@ -1,117 +1,117 @@ -package main - -import ( - "context" - "encoding/json" - "encoding/xml" - "fmt" - "log" - "os" - "strings" - "time" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/pkg/errors" -) - -var ( - logger = log.New(os.Stdout, "", 0) - client dapr.Client - - serviceAddress = getEnvVar("ADDRESS", ":60012") - - sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") - sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "events") - - targetServiceID = getEnvVar("TARGET_SERVICE", "grpc-echo-service") - targetMethodName = getEnvVar("TARGET_METHOD", "echo") - targetFormat = getEnvVar("TARGET_FORMAT", "xml") -) - -func main() { - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - c, err := dapr.NewClient() - if err != nil { - log.Fatalf("failed to create Dapr client: %v", err) - } - client = c - defer client.Close() - - // add handler to the service - sub := &common.Subscription{PubsubName: sourcePubSubName, Topic: sourceTopicName} - s.AddTopicEventHandler(sub, eventHandler) - - // start the server to handle incoming events - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -// SourceEvent represents the input event -type SourceEvent struct { - ID string `json:"id"` - Temperature float64 `json:"temperature"` - Humidity float64 `json:"humidity"` - Time int64 `json:"time"` -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) - - d, ok := e.Data.([]byte) - if !ok { - return false, errors.Errorf("invalid event data type: %T", e.Data) - } - - var se SourceEvent - if err := json.Unmarshal(d, &se); err != nil { - return false, errors.Errorf("error parsing input content: %v", err) - } - - var ( - me error - b []byte - ct string - ) - - switch strings.ToLower(targetFormat) { - case "json": - b = d - ct = "application/json" - case "xml": - if b, me = xml.Marshal(&e); me != nil { - return false, errors.Errorf("error while converting content: %v", me) - } - ct = "application/xml" - case "csv": - b = []byte(fmt.Sprintf(`"%s",%f,%f,"%s"`, - se.ID, se.Temperature, se.Humidity, time.Unix(se.Time, 0).Format(time.RFC3339))) - ct = "text/csv" - default: - return false, errors.Errorf("invalid target format: %s", targetFormat) - } - logger.Printf("Target (%s): %s", targetFormat, b) - - content := &dapr.DataContent{Data: b, ContentType: ct} - out, err := client.InvokeServiceWithContent(ctx, targetServiceID, targetMethodName, content) - if err != nil { - return true, errors.Wrap(err, "error invoking target binding") - } - logger.Printf("Response: %s", out) - - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "encoding/xml" + "fmt" + "log" + "os" + "strings" + "time" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/pkg/errors" +) + +var ( + logger = log.New(os.Stdout, "", 0) + client dapr.Client + + serviceAddress = getEnvVar("ADDRESS", ":60012") + + sourcePubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "fanout-source-pubsub") + sourceTopicName = getEnvVar("SOURCE_TOPIC_NAME", "events") + + targetServiceID = getEnvVar("TARGET_SERVICE", "grpc-echo-service") + targetMethodName = getEnvVar("TARGET_METHOD", "echo") + targetFormat = getEnvVar("TARGET_FORMAT", "xml") +) + +func main() { + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + c, err := dapr.NewClient() + if err != nil { + log.Fatalf("failed to create Dapr client: %v", err) + } + client = c + defer client.Close() + + // add handler to the service + sub := &common.Subscription{PubsubName: sourcePubSubName, Topic: sourceTopicName} + s.AddTopicEventHandler(sub, eventHandler) + + // start the server to handle incoming events + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +// SourceEvent represents the input event +type SourceEvent struct { + ID string `json:"id"` + Temperature float64 `json:"temperature"` + Humidity float64 `json:"humidity"` + Time int64 `json:"time"` +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf("Event - PubsubName:%s, Topic:%s, ID:%s", e.PubsubName, e.Topic, e.ID) + + d, ok := e.Data.([]byte) + if !ok { + return false, errors.Errorf("invalid event data type: %T", e.Data) + } + + var se SourceEvent + if err := json.Unmarshal(d, &se); err != nil { + return false, errors.Errorf("error parsing input content: %v", err) + } + + var ( + me error + b []byte + ct string + ) + + switch strings.ToLower(targetFormat) { + case "json": + b = d + ct = "application/json" + case "xml": + if b, me = xml.Marshal(&e); me != nil { + return false, errors.Errorf("error while converting content: %v", me) + } + ct = "application/xml" + case "csv": + b = []byte(fmt.Sprintf(`"%s",%f,%f,"%s"`, + se.ID, se.Temperature, se.Humidity, time.Unix(se.Time, 0).Format(time.RFC3339))) + ct = "text/csv" + default: + return false, errors.Errorf("invalid target format: %s", targetFormat) + } + logger.Printf("Target (%s): %s", targetFormat, b) + + content := &dapr.DataContent{Data: b, ContentType: ct} + out, err := client.InvokeServiceWithContent(ctx, targetServiceID, targetMethodName, content) + if err != nil { + return true, errors.Wrap(err, "error invoking target binding") + } + logger.Printf("Response: %s", out) + + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/grpc-echo-service/Dockerfile b/grpc-echo-service/Dockerfile index 321a7e5..bc17e76 100644 --- a/grpc-echo-service/Dockerfile +++ b/grpc-echo-service/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.5 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./app . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/app . - -ENTRYPOINT ["./app"] +FROM golang:1.15.5 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./app . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/app . + +ENTRYPOINT ["./app"] diff --git a/grpc-echo-service/Makefile b/grpc-echo-service/Makefile index a9f031a..8df3056 100644 --- a/grpc-echo-service/Makefile +++ b/grpc-echo-service/Makefile @@ -1,65 +1,65 @@ -RELEASE_VERSION =v1.0.1 -SERVICE_NAME ?=echo -IMAGE_NAME ?=grpc-echo-service -IMAGE_TAG ?=v0.1.4 -IMAGE_OWNER ?=$(shell git config --get user.username) - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: test -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60002 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - go run main.go - -.PHONY: invoke -invoke: ## Invokes service through Dapr API - curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - "http://localhost:3500/v1.0/invoke/$(SERVICE_NAME)/method/echo" - -.PHONY: image -image: tidy ## Builds and publish image - docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . - docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" - -.PHONY: call -call: ## Invokes service through Dapr API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/echo" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v1.0.1 +SERVICE_NAME ?=echo +IMAGE_NAME ?=grpc-echo-service +IMAGE_TAG ?=v0.1.4 +IMAGE_OWNER ?=$(shell git config --get user.username) + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: test +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60002 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + go run main.go + +.PHONY: invoke +invoke: ## Invokes service through Dapr API + curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + "http://localhost:3500/v1.0/invoke/$(SERVICE_NAME)/method/echo" + +.PHONY: image +image: tidy ## Builds and publish image + docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . + docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" + +.PHONY: call +call: ## Invokes service through Dapr API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/echo" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/grpc-echo-service/README.md b/grpc-echo-service/README.md index 05baf78..b6d63a7 100644 --- a/grpc-echo-service/README.md +++ b/grpc-echo-service/README.md @@ -1,86 +1,86 @@ -# grpc-service - -For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) - -> You can replicate this demo on any Kubernetes cluster configured with Dapr. To demo the cross-namespace service invocation with external API gateway you will need "dapr'ized' cluster ingress (ingress with Dapr sidecar). You can setup fully configured Dapr cluster with all these dependencies using included [Dapr cluster setup](../setup#dapr-cluster-setup). - -## Run - -To run this demo in Dapr, run: - -```shell -dapr run \ - --app-id echo \ - --app-port 50001 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go -``` - -## Deploy - -To deploy this demo, first setup the `echo` namespace: - -```shell -kubectl apply -f deployment/space.yaml -``` - -Then deploy and wait for the `echo-service` app pod to be ready: - -```shell -kubectl apply -f deployment/app.yaml -kubectl rollout status deployment/echo-service -n echo -``` - -If you have changed an existing component, make sure to reload the ingress and wait until the new version is ready - -```shell -kubectl rollout restart deployment/echo-service -n echo -kubectl rollout status deployment/echo-service -n echo -``` - -Follow logs - -```shell -kubectl logs -l app=echo-service -c service -f -n echo -``` - -In a separate terminal session export API token - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode) -``` - -And invoke the service - -```shell -curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.demo.dapr.team/v1.0/invoke/echo-service.echo/method/echo" -``` - -> Notice the use of `echo-service.echo` namespace in the service invocation - -The response should include the sent message - -```json -{ - "message": "ping" -} -``` - -And the logs - -```shell -Invocation (ContentType:application/json, Verb:POST, QueryString:map[], Data:{ "message": "ping" }) -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# grpc-service + +For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) + +> You can replicate this demo on any Kubernetes cluster configured with Dapr. To demo the cross-namespace service invocation with external API gateway you will need "dapr'ized' cluster ingress (ingress with Dapr sidecar). You can setup fully configured Dapr cluster with all these dependencies using included [Dapr cluster setup](../setup#dapr-cluster-setup). + +## Run + +To run this demo in Dapr, run: + +```shell +dapr run \ + --app-id echo \ + --app-port 50001 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go +``` + +## Deploy + +To deploy this demo, first setup the `echo` namespace: + +```shell +kubectl apply -f deployment/space.yaml +``` + +Then deploy and wait for the `echo-service` app pod to be ready: + +```shell +kubectl apply -f deployment/app.yaml +kubectl rollout status deployment/echo-service -n echo +``` + +If you have changed an existing component, make sure to reload the ingress and wait until the new version is ready + +```shell +kubectl rollout restart deployment/echo-service -n echo +kubectl rollout status deployment/echo-service -n echo +``` + +Follow logs + +```shell +kubectl logs -l app=echo-service -c service -f -n echo +``` + +In a separate terminal session export API token + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode) +``` + +And invoke the service + +```shell +curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.demo.dapr.team/v1.0/invoke/echo-service.echo/method/echo" +``` + +> Notice the use of `echo-service.echo` namespace in the service invocation + +The response should include the sent message + +```json +{ + "message": "ping" +} +``` + +And the logs + +```shell +Invocation (ContentType:application/json, Verb:POST, QueryString:map[], Data:{ "message": "ping" }) +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/grpc-echo-service/deployment/app.yaml b/grpc-echo-service/deployment/app.yaml index 9c34b0c..b5a3e7b 100644 --- a/grpc-echo-service/deployment/app.yaml +++ b/grpc-echo-service/deployment/app.yaml @@ -1,33 +1,33 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: echo-service - namespace: echo - labels: - app: echo-service -spec: - replicas: 1 - selector: - matchLabels: - app: echo-service - template: - metadata: - labels: - app: echo-service - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "echo-service" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "echo-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: ghcr.io/mchmarny/grpc-echo-service:v0.1.4 - ports: - - containerPort: 60002 - env: - - name: ADDRESS +apiVersion: apps/v1 +kind: Deployment +metadata: + name: echo-service + namespace: echo + labels: + app: echo-service +spec: + replicas: 1 + selector: + matchLabels: + app: echo-service + template: + metadata: + labels: + app: echo-service + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "echo-service" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "echo-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: ghcr.io/mchmarny/grpc-echo-service:v0.1.4 + ports: + - containerPort: 60002 + env: + - name: ADDRESS value: ":60002" \ No newline at end of file diff --git a/grpc-echo-service/deployment/space.yaml b/grpc-echo-service/deployment/space.yaml index e0290ea..95cb590 100644 --- a/grpc-echo-service/deployment/space.yaml +++ b/grpc-echo-service/deployment/space.yaml @@ -1,49 +1,49 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: echo ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: echo-config - namespace: echo -spec: - tracing: - samplingRate: "1" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: secret-reader - namespace: echo -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: dapr-secret-reader - namespace: echo -subjects: -- kind: ServiceAccount - name: default -roleRef: - kind: Role - name: secret-reader - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: zipkin - namespace: echo -spec: - type: exporters.zipkin - metadata: - - name: enabled - value: "true" - - name: exporterAddress - value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" +apiVersion: v1 +kind: Namespace +metadata: + name: echo +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: echo-config + namespace: echo +spec: + tracing: + samplingRate: "1" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secret-reader + namespace: echo +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dapr-secret-reader + namespace: echo +subjects: +- kind: ServiceAccount + name: default +roleRef: + kind: Role + name: secret-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: zipkin + namespace: echo +spec: + type: exporters.zipkin + metadata: + - name: enabled + value: "true" + - name: exporterAddress + value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" diff --git a/grpc-echo-service/go.mod b/grpc-echo-service/go.mod index 985bb90..b18dfc0 100644 --- a/grpc-echo-service/go.mod +++ b/grpc-echo-service/go.mod @@ -1,9 +1,9 @@ -module github.com/mchmarny/dapr-demos/grpc-echo-service - -go 1.15 - -require ( - github.com/dapr/go-sdk v1.0.0-rc-1 - golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 // indirect - google.golang.org/genproto v0.0.0-20201116205149-79184cff4dfe // indirect -) +module github.com/mchmarny/dapr-demos/grpc-echo-service + +go 1.15 + +require ( + github.com/dapr/go-sdk v1.0.0-rc-1 + golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 // indirect + google.golang.org/genproto v0.0.0-20201116205149-79184cff4dfe // indirect +) diff --git a/grpc-echo-service/go.sum b/grpc-echo-service/go.sum index 3a7ca38..61e7241 100644 --- a/grpc-echo-service/go.sum +++ b/grpc-echo-service/go.sum @@ -1,117 +1,117 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v1.0.0-rc-1 h1:aBkgeQfrE9VAabvwL094z4RJWwt41gNKAusE2JoOrB4= -github.com/dapr/go-sdk v1.0.0-rc-1/go.mod h1:A53nH+Wc8ahU9+A2Eddz11916eE6opTmwfxAajL0bYo= -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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 h1:AYCWBZhgIw6XobZ5CibNJr0Rc4ZofGGKvWa1vcx2IGk= -golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201112120144-2985b7af83de/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201116205149-79184cff4dfe h1:4zuxUDBUB7MD0/Nb3BD2e2+YHXs2BQxZ9ivB5RJd7ng= -google.golang.org/genproto v0.0.0-20201116205149-79184cff4dfe/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v1.0.0-rc-1 h1:aBkgeQfrE9VAabvwL094z4RJWwt41gNKAusE2JoOrB4= +github.com/dapr/go-sdk v1.0.0-rc-1/go.mod h1:A53nH+Wc8ahU9+A2Eddz11916eE6opTmwfxAajL0bYo= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 h1:AYCWBZhgIw6XobZ5CibNJr0Rc4ZofGGKvWa1vcx2IGk= +golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201112120144-2985b7af83de/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201116205149-79184cff4dfe h1:4zuxUDBUB7MD0/Nb3BD2e2+YHXs2BQxZ9ivB5RJd7ng= +google.golang.org/genproto v0.0.0-20201116205149-79184cff4dfe/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/grpc-echo-service/main.go b/grpc-echo-service/main.go index 57fb14f..06fe629 100644 --- a/grpc-echo-service/main.go +++ b/grpc-echo-service/main.go @@ -1,55 +1,55 @@ -package main - -import ( - "context" - "log" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" -) - -var ( - logger = log.New(os.Stdout, "", 0) - serviceAddress = getEnvVar("ADDRESS", ":60002") -) - -func main() { - // create serving server - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - // add handler to the service - s.AddServiceInvocationHandler("echo", echoHandler) - - // start the server to handle incoming events - log.Printf("starting server at %s...", serviceAddress) - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func echoHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - logger.Printf( - "Invocation (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", - in.ContentType, in.Verb, in.QueryString, string(in.Data), - ) - - // TODO: implement handling logic here - out = &common.Content{ - ContentType: in.ContentType, - Data: in.Data, - } - - return -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" +) + +var ( + logger = log.New(os.Stdout, "", 0) + serviceAddress = getEnvVar("ADDRESS", ":60002") +) + +func main() { + // create serving server + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + // add handler to the service + s.AddServiceInvocationHandler("echo", echoHandler) + + // start the server to handle incoming events + log.Printf("starting server at %s...", serviceAddress) + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func echoHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + logger.Printf( + "Invocation (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", + in.ContentType, in.Verb, in.QueryString, string(in.Data), + ) + + // TODO: implement handling logic here + out = &common.Content{ + ContentType: in.ContentType, + Data: in.Data, + } + + return +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/grpc-event-subscriber/Dockerfile b/grpc-event-subscriber/Dockerfile index f5904ac..a5c01c7 100644 --- a/grpc-event-subscriber/Dockerfile +++ b/grpc-event-subscriber/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/grpc-event-subscriber/Makefile b/grpc-event-subscriber/Makefile index 5a2be54..6524f14 100644 --- a/grpc-event-subscriber/Makefile +++ b/grpc-event-subscriber/Makefile @@ -1,83 +1,83 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=grpc-event-subscriber -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag -all: help - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -debug: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60002 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go - -build: tidy ## Builds local release binary - CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . - -run: build ## Builds binary and runs it in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60002 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - --components-path ./config \ - bin/$(SERVICE_NAME) - -jsonevent: ## Publishes sample JSON message to Dapr pubsub API - curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ - -H "Content-type: application/json" \ - "http://localhost:3500/v1.0/publish/grpc-events/messages" - -xmlevent: ## Publishes sample XML message to Dapr pubsub API - curl -d 'JohnLary' \ - -H "Content-type: application/xml" \ - "http://localhost:3500/v1.0/publish/grpc-events/messages" - -binevent: ## Publishes sample binary message to Dapr pubsub API - curl -d '0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40' \ - -H "Content-type: application/octet-stream" \ - "http://localhost:3500/v1.0/publish/grpc-events/messages" - -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/component.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/grpc-event-subscriber - kubectl rollout restart deployment/nginx-ingress-nginx-controller - kubectl rollout status deployment/nginx-ingress-nginx-controller - -event: ## Publishes sample JSON message to Dapr pubsub API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/publish/grpc-events/messages" - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=grpc-event-subscriber +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag +all: help + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +debug: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60002 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go + +build: tidy ## Builds local release binary + CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . + +run: build ## Builds binary and runs it in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60002 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + --components-path ./config \ + bin/$(SERVICE_NAME) + +jsonevent: ## Publishes sample JSON message to Dapr pubsub API + curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ + -H "Content-type: application/json" \ + "http://localhost:3500/v1.0/publish/grpc-events/messages" + +xmlevent: ## Publishes sample XML message to Dapr pubsub API + curl -d 'JohnLary' \ + -H "Content-type: application/xml" \ + "http://localhost:3500/v1.0/publish/grpc-events/messages" + +binevent: ## Publishes sample binary message to Dapr pubsub API + curl -d '0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40' \ + -H "Content-type: application/octet-stream" \ + "http://localhost:3500/v1.0/publish/grpc-events/messages" + +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/component.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/grpc-event-subscriber + kubectl rollout restart deployment/nginx-ingress-nginx-controller + kubectl rollout status deployment/nginx-ingress-nginx-controller + +event: ## Publishes sample JSON message to Dapr pubsub API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/publish/grpc-events/messages" + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/grpc-event-subscriber/README.md b/grpc-event-subscriber/README.md index fb972b1..ad6c4f8 100644 --- a/grpc-event-subscriber/README.md +++ b/grpc-event-subscriber/README.md @@ -1,85 +1,85 @@ -# grpc-event-subscriber - -## Components - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" - -``` - -For more information about pub/sub see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/publish-subscribe-messaging) - -## Run - -To run this demo in Dapr, run: - -```shell -dapr run --app-id grpc-event-subscriber-demo \ - --app-port 50001 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go -``` - -## Deploy - -Deploy and wait for the pod to be ready - -```shell -kubectl apply -f k8s/component.yaml -kubectl apply -f k8s/deployment.yaml -kubectl rollout status deployment/grpc-event-subscriber -``` - -If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready - -```shell -kubectl rollout restart deployment/nginx-ingress-nginx-controller -kubectl rollout status deployment/nginx-ingress-nginx-controller -``` - -Follow logs - -```shell -kubectl logs -l app=grpc-event-subscriber -c service -f -``` - -In a separate terminal session export API token - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) -``` - -And invoke the service - -```shell -curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/publish/grpc-events/messages" -``` - -In the logs, you should see now an entry similar to this. Feel free to edit the message and try again. - -```shell -event - PubsubName:http-events, Topic:messages, ID:6b6cc665-684d-456c-8880-56e20cdf0519, Data: map[from:John message:hi to:Lary] -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# grpc-event-subscriber + +## Components + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" + +``` + +For more information about pub/sub see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/publish-subscribe-messaging) + +## Run + +To run this demo in Dapr, run: + +```shell +dapr run --app-id grpc-event-subscriber-demo \ + --app-port 50001 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go +``` + +## Deploy + +Deploy and wait for the pod to be ready + +```shell +kubectl apply -f k8s/component.yaml +kubectl apply -f k8s/deployment.yaml +kubectl rollout status deployment/grpc-event-subscriber +``` + +If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready + +```shell +kubectl rollout restart deployment/nginx-ingress-nginx-controller +kubectl rollout status deployment/nginx-ingress-nginx-controller +``` + +Follow logs + +```shell +kubectl logs -l app=grpc-event-subscriber -c service -f +``` + +In a separate terminal session export API token + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) +``` + +And invoke the service + +```shell +curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/publish/grpc-events/messages" +``` + +In the logs, you should see now an entry similar to this. Feel free to edit the message and try again. + +```shell +event - PubsubName:http-events, Topic:messages, ID:6b6cc665-684d-456c-8880-56e20cdf0519, Data: map[from:John message:hi to:Lary] +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/grpc-event-subscriber/config/pubsub.yaml b/grpc-event-subscriber/config/pubsub.yaml index a51ee94..2b42896 100644 --- a/grpc-event-subscriber/config/pubsub.yaml +++ b/grpc-event-subscriber/config/pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: grpc-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: grpc-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/grpc-event-subscriber/go.mod b/grpc-event-subscriber/go.mod index d403a8b..be6890b 100644 --- a/grpc-event-subscriber/go.mod +++ b/grpc-event-subscriber/go.mod @@ -1,5 +1,5 @@ -module github.com/mchmarny/dapr-demos/grpc-event-subscriber - -go 1.15 - -require github.com/dapr/go-sdk v0.11.0 +module github.com/mchmarny/dapr-demos/grpc-event-subscriber + +go 1.15 + +require github.com/dapr/go-sdk v0.11.0 diff --git a/grpc-event-subscriber/go.sum b/grpc-event-subscriber/go.sum index 6bf079a..77d8561 100644 --- a/grpc-event-subscriber/go.sum +++ b/grpc-event-subscriber/go.sum @@ -1,100 +1,100 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/grpc-event-subscriber/k8s/component.yaml b/grpc-event-subscriber/k8s/component.yaml index ff9c0b7..ea94e18 100644 --- a/grpc-event-subscriber/k8s/component.yaml +++ b/grpc-event-subscriber/k8s/component.yaml @@ -1,18 +1,18 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: grpc-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password - - name: allowedTopics - value: "messages" -scopes: -- nginx-ingress +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: grpc-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password + - name: allowedTopics + value: "messages" +scopes: +- nginx-ingress - grpc-event-subscriber \ No newline at end of file diff --git a/grpc-event-subscriber/k8s/deployment.yaml b/grpc-event-subscriber/k8s/deployment.yaml index bab57c5..df016c6 100644 --- a/grpc-event-subscriber/k8s/deployment.yaml +++ b/grpc-event-subscriber/k8s/deployment.yaml @@ -1,37 +1,37 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grpc-event-subscriber - labels: - app: grpc-event-subscriber - demo: grpc-event -spec: - selector: - matchLabels: - app: grpc-event-subscriber - template: - metadata: - labels: - app: grpc-event-subscriber - demo: grpc-event - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "grpc-event-subscriber" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/grpc-event-subscriber:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: ADDRESS - value: ":60002" - - name: PUBSUB_NAME - value: "grpc-events" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-event-subscriber + labels: + app: grpc-event-subscriber + demo: grpc-event +spec: + selector: + matchLabels: + app: grpc-event-subscriber + template: + metadata: + labels: + app: grpc-event-subscriber + demo: grpc-event + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "grpc-event-subscriber" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/grpc-event-subscriber:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: ADDRESS + value: ":60002" + - name: PUBSUB_NAME + value: "grpc-events" + - name: TOPIC_NAME + value: "messages" diff --git a/grpc-event-subscriber/main.go b/grpc-event-subscriber/main.go index cace0b2..6b35856 100644 --- a/grpc-event-subscriber/main.go +++ b/grpc-event-subscriber/main.go @@ -1,56 +1,56 @@ -package main - -import ( - "context" - "log" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" -) - -var ( - logger = log.New(os.Stdout, "", 0) - serviceAddress = getEnvVar("ADDRESS", ":60002") - pubSubName = getEnvVar("PUBSUB_NAME", "grpc-events") - topicName = getEnvVar("TOPIC_NAME", "messages") -) - -func main() { - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - logger.Fatalf("failed to start the server: %v", err) - } - - // add handler to the service - subscription := &common.Subscription{ - PubsubName: pubSubName, - Topic: topicName, - } - s.AddTopicEventHandler(subscription, eventHandler) - - // start the server to handle incoming events - if err := s.Start(); err != nil { - logger.Fatalf("server error: %v", err) - } -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf( - "event - PubsubName:%s, Topic:%s, ID:%s, Data: %s", - e.PubsubName, e.Topic, e.ID, e.Data, - ) - - // TODO: do something with the cloud event data - - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" +) + +var ( + logger = log.New(os.Stdout, "", 0) + serviceAddress = getEnvVar("ADDRESS", ":60002") + pubSubName = getEnvVar("PUBSUB_NAME", "grpc-events") + topicName = getEnvVar("TOPIC_NAME", "messages") +) + +func main() { + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + logger.Fatalf("failed to start the server: %v", err) + } + + // add handler to the service + subscription := &common.Subscription{ + PubsubName: pubSubName, + Topic: topicName, + } + s.AddTopicEventHandler(subscription, eventHandler) + + // start the server to handle incoming events + if err := s.Start(); err != nil { + logger.Fatalf("server error: %v", err) + } +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf( + "event - PubsubName:%s, Topic:%s, ID:%s, Data: %s", + e.PubsubName, e.Topic, e.ID, e.Data, + ) + + // TODO: do something with the cloud event data + + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/hardened/Makefile b/hardened/Makefile index 5380729..961a84d 100644 --- a/hardened/Makefile +++ b/hardened/Makefile @@ -1,36 +1,36 @@ -.PHONY: all -all: help - -.PHONY: apply -apply: ## Applies all and restarts the apps - # apply - kubectl apply -f k8s/ -n hardened - # restart - kubectl rollout restart deployment/app1 -n hardened - kubectl rollout restart deployment/app2 -n hardened - kubectl rollout restart deployment/app3 -n hardened - # status - kubectl rollout status deployment/app1 -n hardened - kubectl rollout status deployment/app2 -n hardened - kubectl rollout status deployment/app3 -n hardened - -.PHONY: ping -ping: ## Execute ping on the Dapr API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -i -d '{ "message": "hello" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - https://api.thingz.io/v1.0/invoke/app1.hardened/method/ping - -.PHONY: count -count: ## Execute counter on the Dapr API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -i -d '{ "on": 1603627556200126373, "count": 2 }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - https://api.thingz.io/v1.0/invoke/app2.hardened/method/counter - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +.PHONY: all +all: help + +.PHONY: apply +apply: ## Applies all and restarts the apps + # apply + kubectl apply -f k8s/ -n hardened + # restart + kubectl rollout restart deployment/app1 -n hardened + kubectl rollout restart deployment/app2 -n hardened + kubectl rollout restart deployment/app3 -n hardened + # status + kubectl rollout status deployment/app1 -n hardened + kubectl rollout status deployment/app2 -n hardened + kubectl rollout status deployment/app3 -n hardened + +.PHONY: ping +ping: ## Execute ping on the Dapr API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -i -d '{ "message": "hello" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + https://api.thingz.io/v1.0/invoke/app1.hardened/method/ping + +.PHONY: count +count: ## Execute counter on the Dapr API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -i -d '{ "on": 1603627556200126373, "count": 2 }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + https://api.thingz.io/v1.0/invoke/app2.hardened/method/counter + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/hardened/README.md b/hardened/README.md index cf78642..a40b8a9 100644 --- a/hardened/README.md +++ b/hardened/README.md @@ -1,350 +1,365 @@ -# Hardening Dapr app demo - -In addition to support for Kubernetes namespace isolation and Role-Based Access Control (RBAC) authorization, Dapr also provides additional, more granular, controls to harden applications deployment in Kubernetes. Some security related features, like in-transit encryption for all sidecar-to-sidecar communication using mutual TLS, are enabled by default. Others, like middleware to apply [Open Policy Agent](https://www.openpolicyagent.org/) (OPA) policies on incoming requests, require opt-in. This demo will overview: - -* Cross-namespace service invocation with logical domain groups trust relationship management -* Secure microservice communication using mTLS and [SPIFFE](https://spiffe.io/) identity verification -* Per operation verb access control settings (e.g. deny all except `POST` from `app2` on `/op1`) -* Per application component scoping (i.e. which app should be able to access a given component) -* Pub/Sub topic scoping (i.e. which app should be able to publish or subscriber to a given topic) -* Application-level secret access control (i.e. which secrets the app should be able to access) -* Token authentication on cluster ingress for both HTTP and gRPC API - -![](img/diagram.png) - -> You can replicate this demo on any Kubernetes cluster configured with Dapr. To demo the cross-namespace service invocation with external API gateway you will need "dapr'ized' cluster ingress (ingress with Dapr sidecar). You can setup fully configured Dapr cluster with all these dependencies using included [Dapr cluster setup](../setup#dapr-cluster-setup). - -## Setup - -In Kubernetes, [namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) provide a way to divide cluster resources between multiple users or applications. To isolate all the microservices in this demo, first, create a namespace on your cluster. - -> For purposes of this demo, the namespace will be called `hardened` but you can choose your own name. - -```shell -kubectl apply -f deployment/namespace -``` - -Also, to illustrate Dapr component scoping (e.g. PubSub and State), this demo will use in-cluster Redis deployment (see [Redis setup](../setup#usage)). To showcase the declarative access control for applications over secrets this demo will use `redis-secret` defined in the `hardened` namespace. - -```shell -kubectl create secret generic redis-secret \ - --from-literal=password="${REDIS_PASS}" \ - -n hardened -``` - -> If this is Redis on your cluster you can look it up using `export REDIS_PASS=$(kubectl get secret -n redis redis -o jsonpath="{.data.redis-password}" | base64 --decode)` and define the `REDIS_PASS` environment variable with that secret. - -Finally, create one more `demo` secret to illustrate later how Dapr controls application's access to secrets. - -```shell -kubectl create secret generic demo-secret --from-literal=demo="demo" -n hardened -``` - -## Deploy - -With the namespace configured and the Redis password created, it's time to deploy: - -* [app1.yaml](./deployment/hardened/app1.yaml), [app2.yaml](./deployment/hardened/app1.yaml), and [app2.yaml](./deployment/hardened/app1.yaml) are the Kubernetes deployments with their Dapr configuration. -* [pubsub.yaml](./deployment/hardened/pubsub.yaml) and [state.yaml](./deployment/hardened/state.yaml) are the configuration files for PubSub and State components using Redis -* [role.yaml](./deployment/hardened/role.yaml) defines the Role and RoleBinding required for Dapr application access the Kubernetes secrets in the `hardened` namespace. - -> This demo uses [prebuilt application images](https://github.com/mchmarny?tab=packages&q=hardened-app). You can review the code for these 3 applications in the [src](./src) directory. - -Now, apply the demo resources to the cluster. - -```shell -kubectl apply -f deployment/hardened -n hardened -``` - -The response from the above command should confirm that all the resources were configured. - -```shell -deployment.apps/app1 configured -configuration.dapr.io/app1-config configured -deployment.apps/app2 configured -configuration.dapr.io/app2-config configured -deployment.apps/app3 configured -configuration.dapr.io/app3-config configured -component.dapr.io/pubsub configured -component.dapr.io/state configured -``` - -## Verify - -To ensure the rest of the demo goes smoothly, check that everything was deployed correctly. - -```shell -kubectl get pods -n hardened -``` - -If everything went well, the response should include `app1`, `app2`, and `app3` pods with the status `Running` and the ready state of `2/2` indicating that the Dapr sidecar has been injected and components successfully loaded. - -```shell -NAME READY STATUS RESTARTS AGE -app1-6df587fb45-k46sz 2/2 Running 0 40s -app2-685fd94f69-5vkwl 2/2 Running 0 40s -app3-6d57778cbd-mxn2k 2/2 Running 0 40s -``` - -## Demo - -The Dapr API exposed on the cluster ingress is protected with [token authentication](https://github.com/dapr/docs/tree/master/howto/enable-dapr-api-token-based-authentication#enable-dapr-apis-token-based-authentication). Start by exporting that token from the cluster secret to allow for API invocation in this demo. - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode) -``` - -### Service Invocation - -The app identity and its access control within Dapr as controlled using [policies](https://github.com/dapr/docs/blob/master/howto/allowlists-serviceinvocation/README.md) which are defined in the app configuration. To "attach" [configuration](https://github.com/dapr/docs/blob/master/howto/allowlists-serviceinvocation/README.md), the app deployment template has to be annotated with the name of the configuration: - -```yaml -annotations: - dapr.io/config: "app1-config" -``` - -In this demo, to allow only the Dapr'ized NGNX ingress to invoke the `/ping` method on [app1.yaml](./deployment/hardened/app1.yaml), the default action is set to `deny` and an explicit policy created for `nginx-ingress` in the `default` namespace which also, first denies access to all methods on that app, and only then allows access on the `/ping` method (aka operation) when the HTTP verb is `POST`. - -```yaml -accessControl: - defaultAction: deny - trustDomain: "hardened" - policies: - - appId: nginx-ingress - namespace: "default" - defaultAction: deny - operations: - - name: /ping - httpVerb: ["POST"] - action: allow -``` - -To demo this now, invoke the `ping` method on `app1` in the `hardened` namespace using the Dapr API exposed on the NGNX ingress. - -> The [Dapr cluster setup](../setup#dapr-cluster-setup) includes custom domain and TLS certificate support. This demo users `api.demo.dapr.team` domain and a wildcard certificates for al (`*`) subdomains. - -```shell -curl -i -d '{ "message": "hello" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - https://api.demo.dapr.team/v1.0/invoke/app1.hardened/method/ping -``` - -Dapr should respond with HTTP status code `200` as well as parent trace ID for this invocation (`traceparent`) in the header, and a JSON payload with the number of API invocations and nano epoch timestamp. - -> The count of API invocations is persisted in the Dapr sate store configured in [State component](./deployment/hardened/state.yaml) - -```shell -HTTP/2 200 -date: Sun, 25 Oct 2020 12:05:56 GMT -content-type: text/plain; charset=utf-8 -content-length: 39 -traceparent: 00-ecbbc473826b3e328ea00f5ac0ce222b-0824d3896092d8ce-01 -strict-transport-security: max-age=15724800; includeSubDomains - -{ "on": 1603627556200126373, "count": 8 } -``` - -To demo the active access policy, try also to invoke the `counter` method on `app2` in the `hardened` namespace. - -```shell -curl -i -d '{ "on": 1603627556200126373, "count": 2 }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - https://api.demo.dapr.team/v1.0/invoke/app2.hardened/method/counter -``` - -That invocation will result in an error. The response will include `PermissionDenied` message: - -```json -{ - "errorCode": "ERR_DIRECT_INVOKE", - "message": "rpc error: code = PermissionDenied desc = access control policy has denied access to appid: app2 operation: ping verb: POST" -} -``` - -The access control defined above applies also to in-cluster invocation ([app2.yaml](./deployment/hardened/app2.yaml)). Where the additional `trustDomain` setting on `app2` configuration is used to only allow access to invoke the `/counter` method when the calling app is `app1`: - -```yaml -policies: - - appId: app1 - defaultAction: deny - trustDomain: "hardened" - namespace: "hardened" - operations: - - name: /counter - httpVerb: ["POST"] - action: allow -``` - -To demo this, forward local port to any other Dapr sidecar besides `app1` in that cluster. - -```shell -kubectl port-forward deployment/app2 3500 -n hardened -``` - -And then try to invoke the `/ping` method on the `app1`. That too will result in `PermissionDenied` message. - -```shell -curl -i -d '{ "message": "hello" }' \ - -H "Content-type: application/json" \ - http://localhost:3500/v1.0/invoke/app1/method/ping -``` - -> In this configuration, all invocations that are not explicitly permitted in Dapr access policy will be denied! - -### Components - - -Just like in case of invocation, access to components in Dapr is also driven by configuration. The [state store](./deployment/hardened/state.yaml) component in this demo is scoped to only be accessible by `app2`: - -```yaml -scopes: -- app2 -``` - -> Dapr automatically isolates state between applications by contextualizing to the app that created it, rendering it inaccessible by any other application regardless of policies. - -### Topic Publishing and Subscription - -The topic access of the PubSub component is further defined by the `publishingScopes` and `subscriptionScopes` lists. In this case `app2` can only publish, and the `app3` can only subscribe to the `messages` topic: - -```yaml -- name: publishingScopes - value: "app2=messages" -- name: subscriptionScopes - value: "app3=messages" -``` - -To demo this, while still forwarding local port to the `app2` pod, try publish to any other topic besides `messages`. - -```shell -curl -i -d '{ "message": "test" }' \ - -H "Content-type: application/json" \ - http://localhost:3500/v1.0/publish/pubsub/test -``` - -The above publish will result in error: - -```json -{ - "errorCode": "ERR_PUBSUB_PUBLISH_MESSAGE", - "message": "topic test is not allowed for app id app2" -} -``` - -You can also try to subscribe to the `messages` topic or even forward port to `app3` and try to publish to the valid topic there, and still receive the same error, because that application is only allowed to subscribe to the `messages` topic, not publish to it. - -### Secrets - -Application access to secrets within Dapr is also driven by configuration. In this demo, the `app2` for example, has its secrets configuration defined as follow: `deny` this application's access to all secrets except `redis-secret`: - -```yaml -secrets: - scopes: - - storeName: kubernetes - defaultAccess: deny - allowedSecrets: ["redis-secret"] -``` - -To demo this, while still forwarding local port to the `app2` pod, and access the `redis-secret`. - -```shell -curl -i "http://localhost:3500/v1.0/secrets/kubernetes/redis-secret?metadata.namespace=hardened" -``` - -Now, try access the other secret we created in the `hardened` namespace during setup: `demo-secret`. - -```shell -curl -i "http://localhost:3500/v1.0/secrets/kubernetes/demo-secret?metadata.namespace=hardened" -``` - -The above query will result in `403 Forbidden` as the `demo-secret` secret is not listed in the `allowedSecrets` list and the `defaultAccess` is set to `deny`. - -```json -{ - "errorCode": "ERR_PERMISSION_DENIED", - "message": "Access denied by policy to get demo-secret from kubernetes" -} -``` - -## Tracing - -Start by generating some requests: - -```shell -for i in {1..100}; do \ - sleep 1; \ - curl -i -d '{ "message": "hello" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - https://api.demo.dapr.team/v1.0/invoke/app1.hardened/method/ping -done -``` - -Then forward the Zipkin port locally: - -```shell -kubectl port-forward svc/zipkin 9411 -n dapr-monitoring -``` - -And navigate to the Zipkin UI to review traces - -http://localhost:9411/ - -![](img/trace.png) - -## Summary - -This demo illustrated just a few of the options that Dapr provides to harden application deployments. For more security-related information (including network, threat model, and latest security audit) see the [Security section](https://docs.dapr.io/concepts/security-concept/) in Dapr documentation. - -## Logging - -To view logs from either the app container (`app`) or Dapr (`daprd`) use the label selector with the app ID (e.g. `app1`, `app2`, or `app3`): - -```shell -kubectl logs -l app=app1 -c daprd -n hardened -``` - -> Because this demo set Dapr logs to be output as JSON you can use [jq](https://stedolan.github.io/jq/) or similar to query the logs - -```shell -kubectl logs -l app=app1 -c daprd -n hardened --tail 300 | jq ".msg" -``` - -Resulting in: - -```shell -"starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" -"log level set to: debug" -"metrics server started on :9090/" -"kubernetes mode configured" -"app id: app1" -``` - -## Restarts - -If you update components you may have to restart the deployments. - -```shell -kubectl rollout restart deployment app1 app2 app3 -n hardened -kubectl rollout status deployment app1 -n hardened -kubectl rollout status deployment app2 -n hardened -kubectl rollout status deployment app3 -n hardened -``` - -## Cleanup - -> By deleting the `hardened` Kubernetes will cascade delete all resources created in that namespace. - -```shell -kubectl delete ns hardened -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Hardening Dapr app demo + +In addition to support for Kubernetes namespace isolation and Role-Based Access Control (RBAC) authorization, Dapr also provides additional, more granular, controls to harden applications deployment in Kubernetes. Some security related features, like in-transit encryption for all sidecar-to-sidecar communication using mutual TLS, are enabled by default. Others, like middleware to apply [Open Policy Agent](https://www.openpolicyagent.org/) (OPA) policies on incoming requests, require opt-in. + +This demo overviews: + +* Cross-namespace service invocation with logical domain groups trust relationship management +* Secure microservice communication using mTLS and [SPIFFE](https://spiffe.io/) identity verification +* Per operation verb access control settings (e.g. deny all except `POST` from `app2` on `/op1`) +* Per application component scoping (i.e. which app should be able to access a given component) +* Pub/Sub topic scoping (i.e. which app should be able to publish or subscriber to a given topic) +* Application-level secret access control (i.e. which secrets the app should be able to access) +* Token authentication on cluster ingress for both HTTP and gRPC API + +![](img/diagram.png) + +> You can replicate this demo on any Kubernetes cluster configured with Dapr. To demo the cross-namespace service invocation with external API gateway you will need "dapr'ized' cluster ingress (ingress with Dapr sidecar). You can setup fully configured Dapr cluster with all these dependencies using included [Dapr cluster setup](../setup#dapr-cluster-setup). + +## Setup + +In Kubernetes, [namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) provide a way to divide cluster resources between multiple users or applications. To isolate all the microservices in this demo, first, create a namespace on your cluster. + +> For purposes of this demo, the namespace will be called `hardened` but you can choose your own name. + +```shell +kubectl apply -f deployment/namespace +``` +### Setup Zipkin monitoring +The sample assumes that Zipkin is deployed into your cluster in the `dapr-monitoring` namespace. + +```shell +kubectl apply -f deployment/monitoring +``` + +### Setup Redis state store +Also, to illustrate Dapr component scoping (e.g. PubSub and State), this demo uses in-cluster Redis deployment (see [Redis setup](https://docs.dapr.io/getting-started/configure-state-pubsub/)). To showcase the declarative access control for applications over secrets this demo uses `redis-secret` defined in the `hardened` namespace. + +The sample assumes that Redis is deployed to your cluster into `redis` namespace. + +```shell +kubectl create secret generic redis-secret \ + --from-literal=password="${REDIS_PASS}" \ + -n hardened +``` + +> If Redis is on your cluster you can look it up using `export REDIS_PASSWORD=$(kubectl get secret -n redis redis -o jsonpath="{.data.redis-password}" | base64 --decode)` and define the `REDIS_PASSWORD` environment variable with that secret. + +### Setup demo secret +Finally, create a `demo` secret to illustrate later how Dapr controls application's access to secrets. + +```shell +kubectl create secret generic demo-secret --from-literal=demo="demo" -n hardened +``` + +## Deploy + +With the namespace configured and the Redis password created, it's time to deploy the application, components and configuration : + +* [app1.yaml](./deployment/hardened/app1.yaml), [app2.yaml](./deployment/hardened/app1.yaml), and [app2.yaml](./deployment/hardened/app1.yaml) are the Kubernetes deployments with their Dapr configuration. +* [pubsub.yaml](./deployment/hardened/pubsub.yaml) and [state.yaml](./deployment/hardened/state.yaml) are the configuration files for PubSub and State components using Redis +* [role.yaml](./deployment/hardened/role.yaml) defines the Role and RoleBinding required for Dapr application access the Kubernetes secrets in the `hardened` namespace. + +> This demo uses [prebuilt application images](https://github.com/mchmarny?tab=packages&q=hardened-app). You can review the code for these 3 applications in the [src](./src) directory. + +Now, apply the demo resources to the cluster. + +```shell +kubectl apply -f deployment/hardened -n hardened +``` + +The response from the above command should confirm that all the resources were configured. + +```shell +deployment.apps/app1 configured +configuration.dapr.io/app1-config configured +deployment.apps/app2 configured +configuration.dapr.io/app2-config configured +deployment.apps/app3 configured +configuration.dapr.io/app3-config configured +component.dapr.io/pubsub configured +component.dapr.io/state configured +``` + +## Verify + +To ensure the rest of the demo goes smoothly, check that everything was deployed correctly. + +```shell +kubectl get pods -n hardened +``` + +If everything went well, the response should include `app1`, `app2`, and `app3` pods with the status `Running` and the ready state of `2/2` indicating that the Dapr sidecar has been injected and components successfully loaded. + +```shell +NAME READY STATUS RESTARTS AGE +app1-6df587fb45-k46sz 2/2 Running 0 40s +app2-685fd94f69-5vkwl 2/2 Running 0 40s +app3-6d57778cbd-mxn2k 2/2 Running 0 40s +``` +If there are errors see how to [view the application logs](logging) +## Demo + +The Dapr API exposed on the cluster ingress is protected with [token authentication](https://github.com/dapr/docs/tree/master/howto/enable-dapr-api-token-based-authentication#enable-dapr-apis-token-based-authentication). Start by exporting that token from the cluster secret to allow for API invocation in this demo. + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode) +``` + +### Service Invocation + +The app identity and its access control within Dapr as controlled using [policies](https://github.com/dapr/docs/blob/master/howto/allowlists-serviceinvocation/README.md) which are defined in the app configuration. To "attach" [configuration](https://github.com/dapr/docs/blob/master/howto/allowlists-serviceinvocation/README.md), the app deployment template has to be annotated with the name of the configuration: + +```yaml +annotations: + dapr.io/config: "app1-config" +``` + +In this demo, to allow only the Dapr'ized NGNX ingress to invoke the `/ping` method on [app1.yaml](./deployment/hardened/app1.yaml), the default action is set to `deny` and an explicit policy created for `nginx-ingress` in the `default` namespace which also, first denies access to all methods on that app, and only then allows access on the `/ping` method (aka operation) when the HTTP verb is `POST`. + +```yaml +accessControl: + defaultAction: deny + trustDomain: "hardened" + policies: + - appId: nginx-ingress + namespace: "default" + defaultAction: deny + operations: + - name: /ping + httpVerb: ["POST"] + action: allow +``` + +To demo this, invoke the `ping` method on `app1` in the `hardened` namespace using the Dapr API exposed on the NGINX ingress. + +> The [Dapr cluster setup](../setup#dapr-cluster-setup) includes custom domain and TLS certificate support. This demo users `api.demo.dapr.team` domain and a wildcard certificates for al (`*`) subdomains. Replace this command with your domain name. + +```shell +curl -i -d '{ "message": "hello" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + https://api.demo.dapr.team/v1.0/invoke/app1.hardened/method/ping +``` + +Dapr should respond with HTTP status code `200` as well as parent trace ID for this invocation (`traceparent`) in the header, and a JSON payload with the number of API invocations and nano epoch timestamp. + +> The count of API invocations is persisted in the Dapr sate store configured in [State component](./deployment/hardened/state.yaml) + +```shell +HTTP/2 200 +date: Sun, 25 Oct 2020 12:05:56 GMT +content-type: text/plain; charset=utf-8 +content-length: 39 +traceparent: 00-ecbbc473826b3e328ea00f5ac0ce222b-0824d3896092d8ce-01 +strict-transport-security: max-age=15724800; includeSubDomains + +{ "on": 1603627556200126373, "count": 8 } +``` + +To demo the active access policy, try to invoke the `counter` method on `app2` in the `hardened` namespace. + +```shell +curl -i -d '{ "on": 1603627556200126373, "count": 2 }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + https://api.demo.dapr.team/v1.0/invoke/app2.hardened/method/counter +``` + +That invocation will result in an error. The response will include `PermissionDenied` message: + +```json +{ + "errorCode": "ERR_DIRECT_INVOKE", + "message": "rpc error: code = PermissionDenied desc = access control policy has denied access to appid: app2 operation: ping verb: POST" +} +``` + +The access control defined above also applies to in-cluster invocation ([app2.yaml](./deployment/hardened/app2.yaml)). Where the additional `trustDomain` setting on `app2` configuration is used to only allow access to invoke the `/counter` method when the calling app is `app1`: + +```yaml +policies: + - appId: app1 + defaultAction: deny + trustDomain: "hardened" + namespace: "hardened" + operations: + - name: /counter + httpVerb: ["POST"] + action: allow +``` + +To demo this, forward a local port to any other Dapr sidecar besides `app1` in that cluster. + +```shell +kubectl port-forward deployment/app2 3500 -n hardened +``` + +And then try to invoke the `/ping` method on the `app1`. That too will result in `PermissionDenied` message. + +```shell +curl -i -d '{ "message": "hello" }' \ + -H "Content-type: application/json" \ + http://localhost:3500/v1.0/invoke/app1/method/ping +``` + +> In this configuration, all invocations that are not explicitly permitted in Dapr access policy are denied! + +### Components + +Just like service invocation, access to components in Dapr is driven by configuration. The [state store](./deployment/hardened/state.yaml) component in this demo is scoped to only be accessible by `app2`: + +```yaml +scopes: +- app2 +``` + +> Dapr automatically isolates state between applications by contextualizing to the app that created it, rendering it inaccessible by any other application regardless of policies. + +### Topic Publishing and Subscription + +The topic access of the PubSub component is further defined by the `publishingScopes` and `subscriptionScopes` lists. In this case `app2` can only publish, and the `app3` can only subscribe to the `messages` topic: + +```yaml +- name: publishingScopes + value: "app2=messages" +- name: subscriptionScopes + value: "app3=messages" +``` + +To demo this, while still forwarding local port to the `app2` pod, try to publish to any other topic besides `messages`. + +```shell +curl -i -d '{ "message": "test" }' \ + -H "Content-type: application/json" \ + http://localhost:3500/v1.0/publish/pubsub/test +``` + +The above publish results in error: + +```json +{ + "errorCode": "ERR_PUBSUB_PUBLISH_MESSAGE", + "message": "topic test is not allowed for app id app2" +} +``` + +You can also try to subscribe to the `messages` topic, forward port to `app3` or try to publish to the valid topic, and will receive the same error, because that application is only allowed to subscribe to the `messages` topic, not publish to it. + +### Secrets + +Application access to secrets within Dapr is also driven by configuration. In this demo, the `app2` for example, has its secrets configuration defined as follow: `deny` this application's access to all secrets except `redis-secret`: + +```yaml +secrets: + scopes: + - storeName: kubernetes + defaultAccess: deny + allowedSecrets: ["redis-secret"] +``` + +To demo this, while still forwarding local port to the `app2` pod, access the `redis-secret` which will be successful. + +```shell +curl -i "http://localhost:3500/v1.0/secrets/kubernetes/redis-secret?metadata.namespace=hardened" +``` + +Now, try access the other secret we created in the `hardened` namespace during setup called `demo-secret`. + +```shell +curl -i "http://localhost:3500/v1.0/secrets/kubernetes/demo-secret?metadata.namespace=hardened" +``` + +This query results in `403 Forbidden` as the `demo-secret` secret is not listed in the `allowedSecrets` list and the `defaultAccess` is set to `deny`. + +```json +{ + "errorCode": "ERR_PERMISSION_DENIED", + "message": "Access denied by policy to get demo-secret from kubernetes" +} +``` + +## Tracing + +Start by generating some requests either with a script or sending several messages from the command prompt. + +```shell +for i in {1..100}; do \ + sleep 1; \ + curl -i -d '{ "message": "hello" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + https://api.demo.dapr.team/v1.0/invoke/app1.hardened/method/ping +done +``` + +Then forward the Zipkin port locally: + +```shell +kubectl port-forward svc/zipkin 9412:9411 -n dapr-monitoring +``` +> Note: If you are using a machine with Dapr installed locally, port 9411 is already taken by the Zipkin Docker container deployed during `dapr init`. Hence choosing port 9412 avoids this potential port clash. + +And navigate to the Zipkin UI to review traces + +http://localhost:9412/ + + +![](img/trace.png) + +## Summary + +This demo illustrated just a few of the options that Dapr provides to harden application deployments. For more security-related information (including network, threat model, and latest security audit) see the [Security section](https://docs.dapr.io/concepts/security-concept/) in Dapr documentation. + +## Logging + +To view logs from either the app container (`app`) or Dapr (`daprd`) use the label selector with the app ID (e.g. `app1`, `app2`, or `app3`): + +```shell +kubectl logs -l app=app1 -c daprd -n hardened + +kubectl logs -l app=app1 -c app -n hardened +``` + +> Because this demo set Dapr logs to be output as JSON you can use [jq](https://stedolan.github.io/jq/) or similar to query the logs + +```shell +kubectl logs -l app=app1 -c daprd -n hardened --tail 300 | jq ".msg" +``` + +Resulting in: + +```shell +"starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" +"log level set to: debug" +"metrics server started on :9090/" +"kubernetes mode configured" +"app id: app1" +``` + +## Restarts + +If you update components you may have to restart the deployments. + +```shell +kubectl rollout restart deployment app1 app2 app3 -n hardened +kubectl rollout status deployment app1 -n hardened +kubectl rollout status deployment app2 -n hardened +kubectl rollout status deployment app3 -n hardened +``` + +## Cleanup + +> By deleting the `hardened` Kubernetes will cascade delete all resources created in that namespace. + +```shell +kubectl delete ns hardened +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/hardened/config/pubsub.yaml b/hardened/config/pubsub.yaml index 6c3f507..d732564 100644 --- a/hardened/config/pubsub.yaml +++ b/hardened/config/pubsub.yaml @@ -1,19 +1,19 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: pubsub - namespace: hardened -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" - - name: publishingScopes - value: "app2=messages" - - name: subscriptionScopes - value: "app3=messages" -scopes: -- app2 +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub + namespace: hardened +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" + - name: publishingScopes + value: "app2=messages" + - name: subscriptionScopes + value: "app3=messages" +scopes: +- app2 - app3 \ No newline at end of file diff --git a/hardened/config/state.yaml b/hardened/config/state.yaml index 675b24c..9d13e9f 100644 --- a/hardened/config/state.yaml +++ b/hardened/config/state.yaml @@ -1,14 +1,14 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: state - namespace: hardened -spec: - type: state.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: state + namespace: hardened +spec: + type: state.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" +scopes: - app2 \ No newline at end of file diff --git a/hardened/deployment/Monitoring/ns.yaml b/hardened/deployment/Monitoring/ns.yaml new file mode 100644 index 0000000..46fd816 --- /dev/null +++ b/hardened/deployment/Monitoring/ns.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: dapr-monitoring \ No newline at end of file diff --git a/hardened/deployment/Monitoring/zipkin.yaml b/hardened/deployment/Monitoring/zipkin.yaml new file mode 100644 index 0000000..fb67886 --- /dev/null +++ b/hardened/deployment/Monitoring/zipkin.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zipkin + namespace: dapr-monitoring + labels: + app: zipkin +spec: + replicas: 1 + selector: + matchLabels: + app: zipkin + template: + metadata: + labels: + app: zipkin + spec: + containers: + - name: zipkin + image: openzipkin/zipkin + ports: + - containerPort: 9411 +--- +kind: Service +apiVersion: v1 +metadata: + name: zipkin + namespace: dapr-monitoring + labels: + app: zipkin +spec: + selector: + app: zipkin + ports: + - protocol: TCP + port: 9411 + targetPort: 9411 + type: ClusterIP diff --git a/hardened/deployment/hardened/app1.yaml b/hardened/deployment/hardened/app1.yaml index 5bedf1d..8fadb74 100644 --- a/hardened/deployment/hardened/app1.yaml +++ b/hardened/deployment/hardened/app1.yaml @@ -1,62 +1,64 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: app1 - namespace: hardened - labels: - app: app1 - demo: hardened -spec: - replicas: 1 - selector: - matchLabels: - app: app1 - template: - metadata: - labels: - app: app1 - demo: hardened - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "app1" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8081" - dapr.io/config: "app1-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: app - image: ghcr.io/mchmarny/hardened-app1:v0.1.4 - ports: - - containerPort: 8081 - env: - - name: PUBSUB_NAME - value: "pubsub" - - name: TOPIC_NAME - value: "messages" ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: app1-config - namespace: hardened -spec: - tracing: - samplingRate: "1" - secrets: - scopes: - - storeName: kubernetes - defaultAccess: deny - accessControl: - defaultAction: deny - trustDomain: "hardened" - policies: - - appId: nginx-ingress - defaultAction: deny - trustDomain: "public" - namespace: "nginx" # where NGINX is installed - operations: - - name: /ping - httpVerb: ["POST"] - action: allow +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app1 + namespace: hardened + labels: + app: app1 + demo: hardened +spec: + replicas: 1 + selector: + matchLabels: + app: app1 + template: + metadata: + labels: + app: app1 + demo: hardened + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "app1" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8081" + dapr.io/config: "app1-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: app + image: ghcr.io/mchmarny/hardened-app1:v0.1.4 + ports: + - containerPort: 8081 + env: + - name: PUBSUB_NAME + value: "pubsub" + - name: TOPIC_NAME + value: "messages" +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: app1-config + namespace: hardened +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" + secrets: + scopes: + - storeName: kubernetes + defaultAccess: deny + accessControl: + defaultAction: deny + trustDomain: "hardened" + policies: + - appId: nginx-ingress + defaultAction: deny + trustDomain: "public" + namespace: "default" # where NGINX is installed + operations: + - name: /ping + httpVerb: ["POST"] + action: allow diff --git a/hardened/deployment/hardened/app2.yaml b/hardened/deployment/hardened/app2.yaml index bd11c59..b676466 100644 --- a/hardened/deployment/hardened/app2.yaml +++ b/hardened/deployment/hardened/app2.yaml @@ -1,65 +1,67 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: app2 - namespace: hardened - labels: - app: app2 - demo: hardened -spec: - replicas: 1 - selector: - matchLabels: - app: app2 - template: - metadata: - labels: - app: app2 - demo: hardened - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "app2" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8082" - dapr.io/config: "app2-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: app - image: ghcr.io/mchmarny/hardened-app2:v0.1.4 - ports: - - containerPort: 8082 - env: - - name: PUBSUB_NAME - value: "pubsub" - - name: TOPIC_NAME - value: "messages" - - name: STORE_NAME - value: "state" ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: app2-config - namespace: hardened -spec: - tracing: - samplingRate: "1" - secrets: - scopes: - - storeName: kubernetes - defaultAccess: deny - allowedSecrets: ["redis-secret"] - accessControl: - defaultAction: deny - trustDomain: "hardened" - policies: - - appId: app1 - defaultAction: deny - trustDomain: "hardened" - namespace: "hardened" - operations: - - name: /counter - httpVerb: ["POST"] - action: allow +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app2 + namespace: hardened + labels: + app: app2 + demo: hardened +spec: + replicas: 1 + selector: + matchLabels: + app: app2 + template: + metadata: + labels: + app: app2 + demo: hardened + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "app2" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8082" + dapr.io/config: "app2-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: app + image: ghcr.io/mchmarny/hardened-app2:v0.1.4 + ports: + - containerPort: 8082 + env: + - name: PUBSUB_NAME + value: "pubsub" + - name: TOPIC_NAME + value: "messages" + - name: STORE_NAME + value: "state" +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: app2-config + namespace: hardened +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" + secrets: + scopes: + - storeName: kubernetes + defaultAccess: deny + allowedSecrets: ["redis-secret"] + accessControl: + defaultAction: deny + trustDomain: "hardened" + policies: + - appId: app1 + defaultAction: deny + trustDomain: "hardened" + namespace: "hardened" + operations: + - name: /counter + httpVerb: ["POST"] + action: allow diff --git a/hardened/deployment/hardened/app3.yaml b/hardened/deployment/hardened/app3.yaml index 5b4cd03..6dd50ae 100644 --- a/hardened/deployment/hardened/app3.yaml +++ b/hardened/deployment/hardened/app3.yaml @@ -1,52 +1,54 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: app3 - namespace: hardened - labels: - app: app3 - demo: hardened -spec: - replicas: 1 - selector: - matchLabels: - app: app3 - template: - metadata: - labels: - app: app3 - demo: hardened - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "app3" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8083" - dapr.io/config: "app3-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: app - image: ghcr.io/mchmarny/hardened-app3:v0.1.4 - ports: - - containerPort: 8083 - env: - - name: PUBSUB_NAME - value: "pubsub" - - name: TOPIC_NAME - value: "messages" ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: app3-config - namespace: hardened -spec: - tracing: - samplingRate: "1" - secrets: - scopes: - - storeName: kubernetes - defaultAccess: deny - allowedSecrets: ["redis-secret"] +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app3 + namespace: hardened + labels: + app: app3 + demo: hardened +spec: + replicas: 1 + selector: + matchLabels: + app: app3 + template: + metadata: + labels: + app: app3 + demo: hardened + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "app3" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8083" + dapr.io/config: "app3-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: app + image: ghcr.io/mchmarny/hardened-app3:v0.1.4 + ports: + - containerPort: 8083 + env: + - name: PUBSUB_NAME + value: "pubsub" + - name: TOPIC_NAME + value: "messages" +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: app3-config + namespace: hardened +spec: + tracing: + samplingRate: "1" + zipkin: + endpointAddress: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" + secrets: + scopes: + - storeName: kubernetes + defaultAccess: deny + allowedSecrets: ["redis-secret"] \ No newline at end of file diff --git a/hardened/deployment/hardened/pubsub.yaml b/hardened/deployment/hardened/pubsub.yaml index c29ee43..e37cf21 100644 --- a/hardened/deployment/hardened/pubsub.yaml +++ b/hardened/deployment/hardened/pubsub.yaml @@ -1,21 +1,21 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: pubsub - namespace: hardened -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password - - name: publishingScopes - value: "app2=messages" - - name: subscriptionScopes - value: "app3=messages" -scopes: -- app2 +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub + namespace: hardened +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password + - name: publishingScopes + value: "app2=messages" + - name: subscriptionScopes + value: "app3=messages" +scopes: +- app2 - app3 \ No newline at end of file diff --git a/hardened/deployment/hardened/state.yaml b/hardened/deployment/hardened/state.yaml index 915c260..3ab1f1d 100644 --- a/hardened/deployment/hardened/state.yaml +++ b/hardened/deployment/hardened/state.yaml @@ -1,16 +1,16 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: state - namespace: hardened -spec: - type: state.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: state + namespace: hardened +spec: + type: state.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password +scopes: - app2 \ No newline at end of file diff --git a/hardened/deployment/namespace/ns.yaml b/hardened/deployment/namespace/ns.yaml index f078bea..c543653 100644 --- a/hardened/deployment/namespace/ns.yaml +++ b/hardened/deployment/namespace/ns.yaml @@ -1,4 +1,4 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: hardened \ No newline at end of file +apiVersion: v1 +kind: Namespace +metadata: + name: hardened diff --git a/hardened/deployment/namespace/role.yaml b/hardened/deployment/namespace/role.yaml index 9ca881d..129f546 100644 --- a/hardened/deployment/namespace/role.yaml +++ b/hardened/deployment/namespace/role.yaml @@ -1,22 +1,22 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: secret-reader - namespace: hardened -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: dapr-secret-reader - namespace: hardened -subjects: -- kind: ServiceAccount - name: default -roleRef: - kind: Role - name: secret-reader +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secret-reader + namespace: hardened +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dapr-secret-reader + namespace: hardened +subjects: +- kind: ServiceAccount + name: default +roleRef: + kind: Role + name: secret-reader apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/hardened/deployment/namespace/trace.yaml b/hardened/deployment/namespace/trace.yaml deleted file mode 100644 index 6e655c3..0000000 --- a/hardened/deployment/namespace/trace.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: zipkin - namespace: hardened -spec: - type: exporters.zipkin - metadata: - - name: enabled - value: "true" - - name: exporterAddress - value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" \ No newline at end of file diff --git a/hardened/deployment/simple/app1.yaml b/hardened/deployment/simple/app1.yaml index c896ac9..a32ecc9 100644 --- a/hardened/deployment/simple/app1.yaml +++ b/hardened/deployment/simple/app1.yaml @@ -1,36 +1,36 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: app1 - namespace: hardened - labels: - app: app1 - demo: hardened -spec: - replicas: 1 - selector: - matchLabels: - app: app1 - template: - metadata: - labels: - app: app1 - demo: hardened - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "app1" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8081" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: app - image: ghcr.io/mchmarny/hardened-app1:v0.1.4 - ports: - - containerPort: 8081 - env: - - name: PUBSUB_NAME - value: "pubsub" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app1 + namespace: hardened + labels: + app: app1 + demo: hardened +spec: + replicas: 1 + selector: + matchLabels: + app: app1 + template: + metadata: + labels: + app: app1 + demo: hardened + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "app1" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8081" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: app + image: ghcr.io/mchmarny/hardened-app1:v0.1.4 + ports: + - containerPort: 8081 + env: + - name: PUBSUB_NAME + value: "pubsub" + - name: TOPIC_NAME + value: "messages" diff --git a/hardened/deployment/simple/app2.yaml b/hardened/deployment/simple/app2.yaml index 3372493..87d569e 100644 --- a/hardened/deployment/simple/app2.yaml +++ b/hardened/deployment/simple/app2.yaml @@ -1,39 +1,39 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: app2 - namespace: hardened - labels: - app: app2 - demo: hardened -spec: - replicas: 1 - selector: - matchLabels: - app: app2 - template: - metadata: - labels: - app: app2 - demo: hardened - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "app2" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8082" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: app - image: ghcr.io/mchmarny/hardened-app2:v0.1.4 - ports: - - containerPort: 8082 - env: - - name: PUBSUB_NAME - value: "pubsub" - - name: TOPIC_NAME - value: "messages" - - name: STORE_NAME - value: "state" - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app2 + namespace: hardened + labels: + app: app2 + demo: hardened +spec: + replicas: 1 + selector: + matchLabels: + app: app2 + template: + metadata: + labels: + app: app2 + demo: hardened + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "app2" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8082" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: app + image: ghcr.io/mchmarny/hardened-app2:v0.1.4 + ports: + - containerPort: 8082 + env: + - name: PUBSUB_NAME + value: "pubsub" + - name: TOPIC_NAME + value: "messages" + - name: STORE_NAME + value: "state" + diff --git a/hardened/deployment/simple/app3.yaml b/hardened/deployment/simple/app3.yaml index 7f86156..7f49aa1 100644 --- a/hardened/deployment/simple/app3.yaml +++ b/hardened/deployment/simple/app3.yaml @@ -1,36 +1,36 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: app3 - namespace: hardened - labels: - app: app3 - demo: hardened -spec: - replicas: 1 - selector: - matchLabels: - app: app3 - template: - metadata: - labels: - app: app3 - demo: hardened - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "app3" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8083" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: app - image: ghcr.io/mchmarny/hardened-app3:v0.1.4 - ports: - - containerPort: 8083 - env: - - name: PUBSUB_NAME - value: "pubsub" - - name: TOPIC_NAME - value: "messages" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app3 + namespace: hardened + labels: + app: app3 + demo: hardened +spec: + replicas: 1 + selector: + matchLabels: + app: app3 + template: + metadata: + labels: + app: app3 + demo: hardened + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "app3" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8083" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: app + image: ghcr.io/mchmarny/hardened-app3:v0.1.4 + ports: + - containerPort: 8083 + env: + - name: PUBSUB_NAME + value: "pubsub" + - name: TOPIC_NAME + value: "messages" diff --git a/hardened/deployment/simple/pubsub.yaml b/hardened/deployment/simple/pubsub.yaml index 5eef04b..a984484 100644 --- a/hardened/deployment/simple/pubsub.yaml +++ b/hardened/deployment/simple/pubsub.yaml @@ -1,14 +1,14 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: pubsub - namespace: hardened -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pubsub + namespace: hardened +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret key: password \ No newline at end of file diff --git a/hardened/deployment/simple/state.yaml b/hardened/deployment/simple/state.yaml index 915c260..3ab1f1d 100644 --- a/hardened/deployment/simple/state.yaml +++ b/hardened/deployment/simple/state.yaml @@ -1,16 +1,16 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: state - namespace: hardened -spec: - type: state.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: state + namespace: hardened +spec: + type: state.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password +scopes: - app2 \ No newline at end of file diff --git a/hardened/src/app1/Dockerfile b/hardened/src/app1/Dockerfile index cc1ecc6..261c8c3 100644 --- a/hardened/src/app1/Dockerfile +++ b/hardened/src/app1/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.3 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./app . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/app . - -ENTRYPOINT ["./app"] +FROM golang:1.15.3 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./app . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/app . + +ENTRYPOINT ["./app"] diff --git a/hardened/src/app1/Makefile b/hardened/src/app1/Makefile index 4ed050c..40f980e 100644 --- a/hardened/src/app1/Makefile +++ b/hardened/src/app1/Makefile @@ -1,48 +1,48 @@ -IMAGE_NAME ?=hardened-app1 -IMAGE_TAG ?=v0.1.4 -IMAGE_OWNER ?=$(shell git config --get user.username) -APP_ID ?=app1 -APP_PORT ?=8081 - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: test -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(APP_ID) \ - --app-port $(APP_PORT) \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ../../config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publish image - docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . - docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +IMAGE_NAME ?=hardened-app1 +IMAGE_TAG ?=v0.1.4 +IMAGE_OWNER ?=$(shell git config --get user.username) +APP_ID ?=app1 +APP_PORT ?=8081 + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: test +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(APP_ID) \ + --app-port $(APP_PORT) \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ../../config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publish image + docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . + docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/hardened/src/app1/go.mod b/hardened/src/app1/go.mod index 736a068..baa42c7 100644 --- a/hardened/src/app1/go.mod +++ b/hardened/src/app1/go.mod @@ -1,8 +1,8 @@ -module github.com/mchmarny/dapr-demos/hardened/src/app1 - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.1 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/hardened/src/app1 + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.1 + github.com/pkg/errors v0.9.1 +) diff --git a/hardened/src/app1/go.sum b/hardened/src/app1/go.sum index 7c8a027..669a06a 100644 --- a/hardened/src/app1/go.sum +++ b/hardened/src/app1/go.sum @@ -1,114 +1,114 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= -github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= -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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= -golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= +github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= +golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hardened/src/app1/main.go b/hardened/src/app1/main.go index 29fe02f..3cb29fd 100644 --- a/hardened/src/app1/main.go +++ b/hardened/src/app1/main.go @@ -1,75 +1,75 @@ -package main - -import ( - "context" - "fmt" - "log" - "os" - "strings" - "time" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" - "github.com/pkg/errors" -) - -const ( - methodName = "ping" - invocationError = "error invoking" -) - -var ( - logger = log.New(os.Stdout, "", 0) - - serviceAddress = getEnvVar("ADDRESS", ":8081") - targetServiceName = getEnvVar("TARGET_SERVICE_NAME", "app2") - targetMethodName = getEnvVar("TARGET_METHOD_NAME", "counter") - - client dapr.Client -) - -func main() { - s := daprd.NewService(serviceAddress) - - var clientErr error - if client, clientErr = dapr.NewClient(); clientErr != nil { - logger.Fatalf("error creating Dapr client: %v", clientErr) - } - defer client.Close() - - if err := s.AddServiceInvocationHandler(methodName, handler); err != nil { - logger.Fatalf("error adding call invocation handler: %v", err) - } - - logger.Printf("starting server at %s...", serviceAddress) - if err := s.Start(); err != nil { - logger.Fatalf("error starting server: %v", err) - } -} - -func handler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - logger.Printf("Method %s invoked (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", - methodName, in.ContentType, in.Verb, in.QueryString, in.Data) - - m := &dapr.DataContent{ContentType: in.ContentType, Data: in.Data} - - data, err := client.InvokeServiceWithContent(ctx, targetServiceName, targetMethodName, m) - if err != nil { - logger.Printf("%s %s/%s: %v", invocationError, targetServiceName, targetMethodName, err) - return nil, errors.Wrapf(err, "%s %s/%s", invocationError, targetServiceName, targetMethodName) - } - logger.Printf("Response from invocation: %s", data) - - j := []byte(fmt.Sprintf(`{"on": %d, "count": %s}`, time.Now().UTC().UnixNano(), string(data))) - - out = &common.Content{ContentType: in.ContentType, Data: j} - return -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "fmt" + "log" + "os" + "strings" + "time" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" + "github.com/pkg/errors" +) + +const ( + methodName = "ping" + invocationError = "error invoking" +) + +var ( + logger = log.New(os.Stdout, "", 0) + + serviceAddress = getEnvVar("ADDRESS", ":8081") + targetServiceName = getEnvVar("TARGET_SERVICE_NAME", "app2") + targetMethodName = getEnvVar("TARGET_METHOD_NAME", "counter") + + client dapr.Client +) + +func main() { + s := daprd.NewService(serviceAddress) + + var clientErr error + if client, clientErr = dapr.NewClient(); clientErr != nil { + logger.Fatalf("error creating Dapr client: %v", clientErr) + } + defer client.Close() + + if err := s.AddServiceInvocationHandler(methodName, handler); err != nil { + logger.Fatalf("error adding call invocation handler: %v", err) + } + + logger.Printf("starting server at %s...", serviceAddress) + if err := s.Start(); err != nil { + logger.Fatalf("error starting server: %v", err) + } +} + +func handler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + logger.Printf("Method %s invoked (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", + methodName, in.ContentType, in.Verb, in.QueryString, in.Data) + + m := &dapr.DataContent{ContentType: in.ContentType, Data: in.Data} + + data, err := client.InvokeServiceWithContent(ctx, targetServiceName, targetMethodName, m) + if err != nil { + logger.Printf("%s %s/%s: %v", invocationError, targetServiceName, targetMethodName, err) + return nil, errors.Wrapf(err, "%s %s/%s", invocationError, targetServiceName, targetMethodName) + } + logger.Printf("Response from invocation: %s", data) + + j := []byte(fmt.Sprintf(`{"on": %d, "count": %s}`, time.Now().UTC().UnixNano(), string(data))) + + out = &common.Content{ContentType: in.ContentType, Data: j} + return +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/hardened/src/app2/Dockerfile b/hardened/src/app2/Dockerfile index cc1ecc6..261c8c3 100644 --- a/hardened/src/app2/Dockerfile +++ b/hardened/src/app2/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.3 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./app . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/app . - -ENTRYPOINT ["./app"] +FROM golang:1.15.3 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./app . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/app . + +ENTRYPOINT ["./app"] diff --git a/hardened/src/app2/Makefile b/hardened/src/app2/Makefile index d1d82c3..f2ff22b 100644 --- a/hardened/src/app2/Makefile +++ b/hardened/src/app2/Makefile @@ -1,46 +1,46 @@ -IMAGE_NAME ?=hardened-app2 -IMAGE_TAG ?=v0.1.4 -IMAGE_OWNER ?=$(shell git config --get user.username) -APP_ID ?=app2 -APP_PORT ?=8082 - -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: test -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(APP_ID) \ - --app-port $(APP_PORT) \ - --app-protocol http \ - --components-path ../../config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publishes image - docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . - docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +IMAGE_NAME ?=hardened-app2 +IMAGE_TAG ?=v0.1.4 +IMAGE_OWNER ?=$(shell git config --get user.username) +APP_ID ?=app2 +APP_PORT ?=8082 + +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: test +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(APP_ID) \ + --app-port $(APP_PORT) \ + --app-protocol http \ + --components-path ../../config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publishes image + docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . + docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/hardened/src/app2/go.mod b/hardened/src/app2/go.mod index dde7104..5e4bc94 100644 --- a/hardened/src/app2/go.mod +++ b/hardened/src/app2/go.mod @@ -1,8 +1,8 @@ -module github.com/mchmarny/dapr-demos/hardened/src/app2 - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.1 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/hardened/src/app2 + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.1 + github.com/pkg/errors v0.9.1 +) diff --git a/hardened/src/app2/go.sum b/hardened/src/app2/go.sum index 7c8a027..669a06a 100644 --- a/hardened/src/app2/go.sum +++ b/hardened/src/app2/go.sum @@ -1,114 +1,114 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= -github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= -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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= -golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= +github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= +golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hardened/src/app2/main.go b/hardened/src/app2/main.go index e20178e..1b06a9c 100644 --- a/hardened/src/app2/main.go +++ b/hardened/src/app2/main.go @@ -1,122 +1,122 @@ -package main - -import ( - "context" - "fmt" - "log" - "strconv" - - "net/http" - "os" - "strings" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" - "github.com/pkg/errors" -) - -const ( - methodName = "counter" - stateCounterKey = "count" - - addInvokeHandlerError = "error adding invocation handler" - startingServiceError = "error starting service" - clientCreateError = "error creating Dapr client" - publisingError = "error publishing" - getStateError = "error getting state" - saveStateError = "error saving state" - intParsingError = "error parsing data" -) - -var ( - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8082") - - pubSubName = getEnvVar("PUBSUB_NAME", "pubsub") - topicName = getEnvVar("TOPIC_NAME", "messages") - - storeName = getEnvVar("STORE_NAME", "state") - - client dapr.Client -) - -func main() { - s := daprd.NewService(address) - - var clientErr error - if client, clientErr = dapr.NewClient(); clientErr != nil { - logger.Fatalf("%s: %v", clientCreateError, clientErr) - } - defer client.Close() - - if err := s.AddServiceInvocationHandler(methodName, handler); err != nil { - logger.Fatalf("%s: %v", addInvokeHandlerError, err) - } - - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("%s: %v", startingServiceError, err) - } -} - -func handler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - logger.Printf("Method %s invoked (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", - methodName, in.ContentType, in.Verb, in.QueryString, string(in.Data)) - - // get previous number - data, err := client.GetState(ctx, storeName, stateCounterKey) - if err != nil { - logger.Printf("%s: %v", getStateError, err) - return nil, errors.Wrap(err, getStateError) - } - - // convert the content to number - var count int64 = 0 - var cnvErr error - if data != nil && data.Value != nil { - count, cnvErr = strconv.ParseInt(fmt.Sprintf("%s", data.Value), 10, 64) - if cnvErr != nil { - logger.Printf("%s %v: %v", intParsingError, data.Value, cnvErr) - return nil, errors.Wrap(cnvErr, intParsingError) - } - } - - // increment - count = count + 1 - logger.Printf("New count: %d", count) - - // data from counter - b := []byte(fmt.Sprintf("%d", count)) - - // save new number - item := &dapr.SetStateItem{ - Etag: data.Etag, // using the retreaved etag to ensure count consistency - Key: stateCounterKey, - Value: b, - Options: &dapr.StateOptions{ - Concurrency: dapr.StateConcurrencyLastWrite, - Consistency: dapr.StateConsistencyStrong, - }, - } - if err := client.SaveStateItems(ctx, storeName, item); err != nil { - logger.Printf("%s: %v", saveStateError, err) - return nil, errors.Wrap(err, saveStateError) - } - - // publish the results for processing down the stream - if err := client.PublishEvent(ctx, pubSubName, topicName, b); err != nil { - logger.Printf("%s to %s/%s: %v", publisingError, pubSubName, topicName, err) - return nil, errors.Wrapf(err, "%s to %s/%s", publisingError, pubSubName, topicName) - } - - out = &common.Content{ContentType: in.ContentType, Data: b} - - return -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "fmt" + "log" + "strconv" + + "net/http" + "os" + "strings" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" + "github.com/pkg/errors" +) + +const ( + methodName = "counter" + stateCounterKey = "count" + + addInvokeHandlerError = "error adding invocation handler" + startingServiceError = "error starting service" + clientCreateError = "error creating Dapr client" + publisingError = "error publishing" + getStateError = "error getting state" + saveStateError = "error saving state" + intParsingError = "error parsing data" +) + +var ( + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8082") + + pubSubName = getEnvVar("PUBSUB_NAME", "pubsub") + topicName = getEnvVar("TOPIC_NAME", "messages") + + storeName = getEnvVar("STORE_NAME", "state") + + client dapr.Client +) + +func main() { + s := daprd.NewService(address) + + var clientErr error + if client, clientErr = dapr.NewClient(); clientErr != nil { + logger.Fatalf("%s: %v", clientCreateError, clientErr) + } + defer client.Close() + + if err := s.AddServiceInvocationHandler(methodName, handler); err != nil { + logger.Fatalf("%s: %v", addInvokeHandlerError, err) + } + + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("%s: %v", startingServiceError, err) + } +} + +func handler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + logger.Printf("Method %s invoked (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", + methodName, in.ContentType, in.Verb, in.QueryString, string(in.Data)) + + // get previous number + data, err := client.GetState(ctx, storeName, stateCounterKey) + if err != nil { + logger.Printf("%s: %v", getStateError, err) + return nil, errors.Wrap(err, getStateError) + } + + // convert the content to number + var count int64 = 0 + var cnvErr error + if data != nil && data.Value != nil { + count, cnvErr = strconv.ParseInt(fmt.Sprintf("%s", data.Value), 10, 64) + if cnvErr != nil { + logger.Printf("%s %v: %v", intParsingError, data.Value, cnvErr) + return nil, errors.Wrap(cnvErr, intParsingError) + } + } + + // increment + count = count + 1 + logger.Printf("New count: %d", count) + + // data from counter + b := []byte(fmt.Sprintf("%d", count)) + + // save new number + item := &dapr.SetStateItem{ + Etag: data.Etag, // using the retreaved etag to ensure count consistency + Key: stateCounterKey, + Value: b, + Options: &dapr.StateOptions{ + Concurrency: dapr.StateConcurrencyLastWrite, + Consistency: dapr.StateConsistencyStrong, + }, + } + if err := client.SaveStateItems(ctx, storeName, item); err != nil { + logger.Printf("%s: %v", saveStateError, err) + return nil, errors.Wrap(err, saveStateError) + } + + // publish the results for processing down the stream + if err := client.PublishEvent(ctx, pubSubName, topicName, b); err != nil { + logger.Printf("%s to %s/%s: %v", publisingError, pubSubName, topicName, err) + return nil, errors.Wrapf(err, "%s to %s/%s", publisingError, pubSubName, topicName) + } + + out = &common.Content{ContentType: in.ContentType, Data: b} + + return +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/hardened/src/app3/Dockerfile b/hardened/src/app3/Dockerfile index cc1ecc6..261c8c3 100644 --- a/hardened/src/app3/Dockerfile +++ b/hardened/src/app3/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.3 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./app . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/app . - -ENTRYPOINT ["./app"] +FROM golang:1.15.3 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./app . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/app . + +ENTRYPOINT ["./app"] diff --git a/hardened/src/app3/Makefile b/hardened/src/app3/Makefile index fce6998..8d756de 100644 --- a/hardened/src/app3/Makefile +++ b/hardened/src/app3/Makefile @@ -1,46 +1,46 @@ -IMAGE_NAME ?=hardened-app3 -IMAGE_TAG ?=v0.1.4 -IMAGE_OWNER ?=$(shell git config --get user.username) -APP_ID ?=app3 -APP_PORT ?=8083 - -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: test -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(APP_ID) \ - --app-port $(APP_PORT) \ - --app-protocol http \ - --components-path ../../config \ - --log-level debug \ - go run main.go - -.PHONY: image -image: tidy ## Builds and publishes image - docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . - docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +IMAGE_NAME ?=hardened-app3 +IMAGE_TAG ?=v0.1.4 +IMAGE_OWNER ?=$(shell git config --get user.username) +APP_ID ?=app3 +APP_PORT ?=8083 + +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: test +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(APP_ID) \ + --app-port $(APP_PORT) \ + --app-protocol http \ + --components-path ../../config \ + --log-level debug \ + go run main.go + +.PHONY: image +image: tidy ## Builds and publishes image + docker build -t "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" . + docker push "ghcr.io/$(IMAGE_OWNER)/$(IMAGE_NAME):$(IMAGE_TAG)" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/hardened/src/app3/go.mod b/hardened/src/app3/go.mod index 9674897..da64d9c 100644 --- a/hardened/src/app3/go.mod +++ b/hardened/src/app3/go.mod @@ -1,8 +1,8 @@ -module github.com/mchmarny/dapr-demos/hardened/src/app3 - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.1 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/hardened/src/app3 + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.1 + github.com/pkg/errors v0.9.1 +) diff --git a/hardened/src/app3/go.sum b/hardened/src/app3/go.sum index 7c8a027..669a06a 100644 --- a/hardened/src/app3/go.sum +++ b/hardened/src/app3/go.sum @@ -1,114 +1,114 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= -github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= -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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= -golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= -golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= -google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.1 h1:83lLYLrOKK1dAI1XhuamY9SkQoo2t5Fd8zlNlDMtwWA= +github.com/dapr/go-sdk v0.11.1/go.mod h1:l9aJ4Zqsfzi9adwheYgFND6ZxhPDbb34IF/NecuPYJo= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= +golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc= +golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM= +google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hardened/src/app3/main.go b/hardened/src/app3/main.go index 2bce6c8..8416896 100644 --- a/hardened/src/app3/main.go +++ b/hardened/src/app3/main.go @@ -1,76 +1,76 @@ -package main - -import ( - "context" - "fmt" - "log" - "strconv" - - "net/http" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" - "github.com/pkg/errors" -) - -const ( - eventDataParsingError = "error parsing event data" - addSubscriptionError = "error adding topic subscription" - startingServiceError = "error starting service" - intParsingError = "error parsing data" -) - -var ( - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8083") - - pubSubName = getEnvVar("PUBSUB_NAME", "pubsub") - topicName = getEnvVar("TOPIC_NAME", "messages") -) - -func main() { - s := daprd.NewService(address) - - subscription := &common.Subscription{ - PubsubName: pubSubName, - Topic: topicName, - Route: fmt.Sprintf("/%s", topicName), - } - - if err := s.AddTopicEventHandler(subscription, handler); err != nil { - logger.Fatalf("%s: %v", addSubscriptionError, err) - } - - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("%s: %v", startingServiceError, err) - } -} - -func handler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf("Event received (PubsubName:%s, Topic:%s, Data: %v", e.PubsubName, e.Topic, e.Data) - - var ( - count int64 = 0 - cnvErr error - ) - - data := fmt.Sprintf("%v", e.Data) - count, cnvErr = strconv.ParseInt(data, 10, 64) - if cnvErr != nil { - logger.Printf("%s %v: %v", intParsingError, data, cnvErr) - return false, errors.Wrap(cnvErr, intParsingError) - } - - logger.Printf("Even: %v", count%2 == 0) - - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "fmt" + "log" + "strconv" + + "net/http" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" + "github.com/pkg/errors" +) + +const ( + eventDataParsingError = "error parsing event data" + addSubscriptionError = "error adding topic subscription" + startingServiceError = "error starting service" + intParsingError = "error parsing data" +) + +var ( + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8083") + + pubSubName = getEnvVar("PUBSUB_NAME", "pubsub") + topicName = getEnvVar("TOPIC_NAME", "messages") +) + +func main() { + s := daprd.NewService(address) + + subscription := &common.Subscription{ + PubsubName: pubSubName, + Topic: topicName, + Route: fmt.Sprintf("/%s", topicName), + } + + if err := s.AddTopicEventHandler(subscription, handler); err != nil { + logger.Fatalf("%s: %v", addSubscriptionError, err) + } + + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("%s: %v", startingServiceError, err) + } +} + +func handler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf("Event received (PubsubName:%s, Topic:%s, Data: %v", e.PubsubName, e.Topic, e.Data) + + var ( + count int64 = 0 + cnvErr error + ) + + data := fmt.Sprintf("%v", e.Data) + count, cnvErr = strconv.ParseInt(data, 10, 64) + if cnvErr != nil { + logger.Printf("%s %v: %v", intParsingError, data, cnvErr) + return false, errors.Wrap(cnvErr, intParsingError) + } + + logger.Printf("Even: %v", count%2 == 0) + + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/http-echo-service/Dockerfile b/http-echo-service/Dockerfile index f5904ac..a5c01c7 100644 --- a/http-echo-service/Dockerfile +++ b/http-echo-service/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/http-echo-service/Makefile b/http-echo-service/Makefile index f542010..f6e2ee5 100644 --- a/http-echo-service/Makefile +++ b/http-echo-service/Makefile @@ -1,69 +1,69 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=http-echo-service -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: all -all: help - -.PHONY: tidy -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -.PHONY: test -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -.PHONY: run -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 8080 \ - --app-protocol http \ - --dapr-http-port 3500 \ - go run main.go - -.PHONY: invoke -invoke: ## Invokes service through Dapr API - curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - "http://localhost:3500/v1.0/invoke/$(SERVICE_NAME)/method/echo" - -.PHONY: image -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -.PHONY: deploy -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f deployment.yaml - kubectl rollout restart deployment/grpc-echo-service - kubectl rollout status deployment/grpc-echo-service - -.PHONY: call -call: ## Invokes service through Dapr API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/echo" - -.PHONY: lint -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -.PHONY: tag -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -.PHONY: clean -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=http-echo-service +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: all +all: help + +.PHONY: tidy +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +.PHONY: test +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +.PHONY: run +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 8080 \ + --app-protocol http \ + --dapr-http-port 3500 \ + go run main.go + +.PHONY: invoke +invoke: ## Invokes service through Dapr API + curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + "http://localhost:3500/v1.0/invoke/$(SERVICE_NAME)/method/echo" + +.PHONY: image +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +.PHONY: deploy +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f deployment.yaml + kubectl rollout restart deployment/grpc-echo-service + kubectl rollout status deployment/grpc-echo-service + +.PHONY: call +call: ## Invokes service through Dapr API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/echo" + +.PHONY: lint +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +.PHONY: tag +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +.PHONY: clean +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/http-echo-service/README.md b/http-echo-service/README.md index 36b04ed..f0d6a99 100644 --- a/http-echo-service/README.md +++ b/http-echo-service/README.md @@ -1,76 +1,76 @@ -# http-service - -For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) - -## Run - -To run this demo in Dapr, run: - -```shell -dapr run \ - --app-id http-service-demo \ - --app-port 8080 \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go -``` - -## Deploy - -Deploy and wait for the pod to be ready - -```shell -kubectl apply -f deployment.yaml -kubectl rollout status deployment/http-echo-service -``` - -If you have changed an existing component, make sure to reload the ingress and wait until the new version is ready - -```shell -kubectl rollout restart deployment/nginx-ingress-nginx-controller -kubectl rollout status deployment/nginx-ingress-nginx-controller -``` - -Follow logs - -```shell -kubectl logs -l app=http-echo-service -c service -f -``` - -In a separate terminal session export API token - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) -``` - -And invoke the service - -```shell -curl -d '{ "message": "ping" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/invoke/http-echo-service/method/echo" -``` - -The response should include the sent message - -```json -{ - "message": "ping" -} -``` - -And the logs - -```shell -Invocation (ContentType:application/json, Verb:POST, QueryString:map[], Data:{ "message": "ping" }) -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# http-service + +For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) + +## Run + +To run this demo in Dapr, run: + +```shell +dapr run \ + --app-id http-service-demo \ + --app-port 8080 \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go +``` + +## Deploy + +Deploy and wait for the pod to be ready + +```shell +kubectl apply -f deployment.yaml +kubectl rollout status deployment/http-echo-service +``` + +If you have changed an existing component, make sure to reload the ingress and wait until the new version is ready + +```shell +kubectl rollout restart deployment/nginx-ingress-nginx-controller +kubectl rollout status deployment/nginx-ingress-nginx-controller +``` + +Follow logs + +```shell +kubectl logs -l app=http-echo-service -c service -f +``` + +In a separate terminal session export API token + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) +``` + +And invoke the service + +```shell +curl -d '{ "message": "ping" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/invoke/http-echo-service/method/echo" +``` + +The response should include the sent message + +```json +{ + "message": "ping" +} +``` + +And the logs + +```shell +Invocation (ContentType:application/json, Verb:POST, QueryString:map[], Data:{ "message": "ping" }) +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/http-echo-service/deployment.yaml b/http-echo-service/deployment.yaml index be97b70..e192601 100644 --- a/http-echo-service/deployment.yaml +++ b/http-echo-service/deployment.yaml @@ -1,35 +1,35 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: http-echo-service - labels: - app: http-echo-service - demo: echo -spec: - replicas: 1 - selector: - matchLabels: - app: http-echo-service - template: - metadata: - labels: - app: http-echo-service - demo: echo - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "http-echo-service" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8080" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/http-echo-service:v0.11.1 - imagePullPolicy: Always - ports: - - containerPort: 8080 - env: - - name: ADDRESS +apiVersion: apps/v1 +kind: Deployment +metadata: + name: http-echo-service + labels: + app: http-echo-service + demo: echo +spec: + replicas: 1 + selector: + matchLabels: + app: http-echo-service + template: + metadata: + labels: + app: http-echo-service + demo: echo + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "http-echo-service" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8080" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/http-echo-service:v0.11.1 + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + - name: ADDRESS value: ":8080" \ No newline at end of file diff --git a/http-echo-service/go.mod b/http-echo-service/go.mod index 910ac94..88984b2 100644 --- a/http-echo-service/go.mod +++ b/http-echo-service/go.mod @@ -1,5 +1,5 @@ -module github.com/mchmarny/dapr-demos/http-echo-service - -go 1.15 - -require github.com/dapr/go-sdk v0.11.0 +module github.com/mchmarny/dapr-demos/http-echo-service + +go 1.15 + +require github.com/dapr/go-sdk v0.11.0 diff --git a/http-echo-service/go.sum b/http-echo-service/go.sum index 4709425..8c33a16 100644 --- a/http-echo-service/go.sum +++ b/http-echo-service/go.sum @@ -1,96 +1,96 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/http-echo-service/main.go b/http-echo-service/main.go index 6d0071b..b394813 100644 --- a/http-echo-service/main.go +++ b/http-echo-service/main.go @@ -1,52 +1,52 @@ -package main - -import ( - "context" - "log" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" -) - -var ( - logger = log.New(os.Stdout, "", 0) - serviceAddress = getEnvVar("ADDRESS", ":8080") -) - -func main() { - // create serving server - s := daprd.NewService(serviceAddress) - - // add handler to the service - s.AddServiceInvocationHandler("echo", echoHandler) - - // start the server to handle incoming events - log.Printf("starting server at %s...", serviceAddress) - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func echoHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - logger.Printf( - "Invocation (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", - in.ContentType, in.Verb, in.QueryString, string(in.Data), - ) - - // TODO: implement handling logic here - out = &common.Content{ - ContentType: in.ContentType, - Data: in.Data, - } - - return -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" +) + +var ( + logger = log.New(os.Stdout, "", 0) + serviceAddress = getEnvVar("ADDRESS", ":8080") +) + +func main() { + // create serving server + s := daprd.NewService(serviceAddress) + + // add handler to the service + s.AddServiceInvocationHandler("echo", echoHandler) + + // start the server to handle incoming events + log.Printf("starting server at %s...", serviceAddress) + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func echoHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + logger.Printf( + "Invocation (ContentType:%s, Verb:%s, QueryString:%s, Data:%s)", + in.ContentType, in.Verb, in.QueryString, string(in.Data), + ) + + // TODO: implement handling logic here + out = &common.Content{ + ContentType: in.ContentType, + Data: in.Data, + } + + return +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/http-event-subscriber/Dockerfile b/http-event-subscriber/Dockerfile index f5904ac..a5c01c7 100644 --- a/http-event-subscriber/Dockerfile +++ b/http-event-subscriber/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/http-event-subscriber/Makefile b/http-event-subscriber/Makefile index 4b4906c..6071a88 100644 --- a/http-event-subscriber/Makefile +++ b/http-event-subscriber/Makefile @@ -1,90 +1,90 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=http-event-subscriber -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag -all: help - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -debug: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 8080 \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ./config \ - --log-level debug \ - go run main.go - -build: tidy ## Builds local release binary - CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . - -run: build ## Builds binary and runs it in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 8080 \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ./config \ - --log-level debug \ - bin/$(SERVICE_NAME) - -ce: ## Publishes CloudEvent message to Dapr pubsub API - curl -d@clooudevent.json \ - -H "Content-type: application/json" \ - "http://localhost:3500/v1.0/publish/http-events/messages" - -jsonevent: ## Publishes sample JSON message to Dapr pubsub API - curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ - -H "Content-type: application/json" \ - "http://localhost:3500/v1.0/publish/http-events/messages" - -xmlevent: ## Publishes sample XML message to Dapr pubsub API - curl -d 'JohnLary' \ - -H "Content-type: application/xml" \ - "http://localhost:3500/v1.0/publish/http-events/messages" - -binevent: ## Publishes sample binary message to Dapr pubsub API - curl -d '0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40' \ - -H "Content-type: application/octet-stream" \ - "http://localhost:3500/v1.0/publish/http-events/messages" - -image: tidy ## Builds and publishes docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/component.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/http-event-subscriber - kubectl rollout restart deployment/nginx-ingress-nginx-controller - kubectl rollout status deployment/nginx-ingress-nginx-controller - -event: ## Publishes sample JSON message to Dapr pubsub API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/publish/http-events/messages" - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=http-event-subscriber +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag +all: help + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +debug: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 8080 \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ./config \ + --log-level debug \ + go run main.go + +build: tidy ## Builds local release binary + CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . + +run: build ## Builds binary and runs it in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 8080 \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ./config \ + --log-level debug \ + bin/$(SERVICE_NAME) + +ce: ## Publishes CloudEvent message to Dapr pubsub API + curl -d@clooudevent.json \ + -H "Content-type: application/json" \ + "http://localhost:3500/v1.0/publish/http-events/messages" + +jsonevent: ## Publishes sample JSON message to Dapr pubsub API + curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ + -H "Content-type: application/json" \ + "http://localhost:3500/v1.0/publish/http-events/messages" + +xmlevent: ## Publishes sample XML message to Dapr pubsub API + curl -d 'JohnLary' \ + -H "Content-type: application/xml" \ + "http://localhost:3500/v1.0/publish/http-events/messages" + +binevent: ## Publishes sample binary message to Dapr pubsub API + curl -d '0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40' \ + -H "Content-type: application/octet-stream" \ + "http://localhost:3500/v1.0/publish/http-events/messages" + +image: tidy ## Builds and publishes docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/component.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/http-event-subscriber + kubectl rollout restart deployment/nginx-ingress-nginx-controller + kubectl rollout status deployment/nginx-ingress-nginx-controller + +event: ## Publishes sample JSON message to Dapr pubsub API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/publish/http-events/messages" + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/http-event-subscriber/README.md b/http-event-subscriber/README.md index 006c5d0..f4833a2 100644 --- a/http-event-subscriber/README.md +++ b/http-event-subscriber/README.md @@ -1,87 +1,87 @@ -# dapr-event-subscriber-template - -## Components - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" - -``` - -For more information about pub/sub see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/publish-subscribe-messaging) - -## Run - -To run this demo in Dapr, run: - -```shell -dapr run \ - --app-id grpc-event-subscriber-demo \ - --app-port 50001 \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go -``` - - -## Deploy - -Deploy and wait for the pod to be ready - -```shell -kubectl apply -f k8s/component.yaml -kubectl apply -f k8s/deployment.yaml -kubectl rollout status deployment/nginx-ingress-nginx-controller -``` - -If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready - -```shell -kubectl rollout restart deployment/nginx-ingress-nginx-controller -kubectl rollout status deployment/nginx-ingress-nginx-controller -``` - -Follow logs - -```shell -kubectl logs -l app=http-event-subscriber -c service -f -``` - -In a separate terminal session export API token - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) -``` - -And invoke the service - -```shell -curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/publish/http-events/messages" -``` - -In the logs, you should see now an entry similar to this. Feel free to edit the message and try again. - -```shell -event - PubsubName:http-events, Topic:messages, ID:6b6cc665-684d-456c-8880-56e20cdf0519, Data: map[from:John message:hi to:Lary] -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# dapr-event-subscriber-template + +## Components + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" + +``` + +For more information about pub/sub see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/publish-subscribe-messaging) + +## Run + +To run this demo in Dapr, run: + +```shell +dapr run \ + --app-id grpc-event-subscriber-demo \ + --app-port 50001 \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go +``` + + +## Deploy + +Deploy and wait for the pod to be ready + +```shell +kubectl apply -f k8s/component.yaml +kubectl apply -f k8s/deployment.yaml +kubectl rollout status deployment/nginx-ingress-nginx-controller +``` + +If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready + +```shell +kubectl rollout restart deployment/nginx-ingress-nginx-controller +kubectl rollout status deployment/nginx-ingress-nginx-controller +``` + +Follow logs + +```shell +kubectl logs -l app=http-event-subscriber -c service -f +``` + +In a separate terminal session export API token + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) +``` + +And invoke the service + +```shell +curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/publish/http-events/messages" +``` + +In the logs, you should see now an entry similar to this. Feel free to edit the message and try again. + +```shell +event - PubsubName:http-events, Topic:messages, ID:6b6cc665-684d-456c-8880-56e20cdf0519, Data: map[from:John message:hi to:Lary] +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/http-event-subscriber/clooudevent.json b/http-event-subscriber/clooudevent.json index 3804997..00e81ff 100644 --- a/http-event-subscriber/clooudevent.json +++ b/http-event-subscriber/clooudevent.json @@ -1,12 +1,12 @@ -{ - "specversion" : "1.0", - "type" : "some.other.operation", - "source" : "https://domain.com/cloudevents/operation", - "subject" : "test operation", - "id" : "id-1234-5678-9011", - "time" : "2020-09-23T06:23:21Z", - "comexampleextension1" : "value", - "comexampleothervalue" : 5, - "datacontenttype" : "text/xml", - "data" : "User1user2hi" +{ + "specversion" : "1.0", + "type" : "some.other.operation", + "source" : "https://domain.com/cloudevents/operation", + "subject" : "test operation", + "id" : "id-1234-5678-9011", + "time" : "2020-09-23T06:23:21Z", + "comexampleextension1" : "value", + "comexampleothervalue" : 5, + "datacontenttype" : "text/xml", + "data" : "User1user2hi" } \ No newline at end of file diff --git a/http-event-subscriber/config/pubsub.yaml b/http-event-subscriber/config/pubsub.yaml index 81f1fe7..0547c13 100644 --- a/http-event-subscriber/config/pubsub.yaml +++ b/http-event-subscriber/config/pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: http-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: http-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/http-event-subscriber/go.mod b/http-event-subscriber/go.mod index 01cce7f..0852c94 100644 --- a/http-event-subscriber/go.mod +++ b/http-event-subscriber/go.mod @@ -1,5 +1,5 @@ -module github.com/mchmarny/dapr-demos/http-event-subscriber - -go 1.15 - -require github.com/dapr/go-sdk v0.11.0 +module github.com/mchmarny/dapr-demos/http-event-subscriber + +go 1.15 + +require github.com/dapr/go-sdk v0.11.0 diff --git a/http-event-subscriber/go.sum b/http-event-subscriber/go.sum index 4709425..8c33a16 100644 --- a/http-event-subscriber/go.sum +++ b/http-event-subscriber/go.sum @@ -1,96 +1,96 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/http-event-subscriber/k8s/component.yaml b/http-event-subscriber/k8s/component.yaml index 826e0d9..4dd1296 100644 --- a/http-event-subscriber/k8s/component.yaml +++ b/http-event-subscriber/k8s/component.yaml @@ -1,18 +1,18 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: http-events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password - - name: allowedTopics - value: "messages" -scopes: -- nginx-ingress +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: http-events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password + - name: allowedTopics + value: "messages" +scopes: +- nginx-ingress - http-event-subscriber \ No newline at end of file diff --git a/http-event-subscriber/k8s/deployment.yaml b/http-event-subscriber/k8s/deployment.yaml index 91a87ad..eb451e8 100644 --- a/http-event-subscriber/k8s/deployment.yaml +++ b/http-event-subscriber/k8s/deployment.yaml @@ -1,38 +1,38 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: http-event-subscriber - labels: - app: http-event-subscriber - demo: http-event -spec: - selector: - matchLabels: - app: http-event-subscriber - template: - metadata: - labels: - app: http-event-subscriber - demo: http-event - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "http-event-subscriber" - dapr.io/app-port: "8080" - dapr.io/config: "tracing" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/http-event-subscriber:v0.11.1 - ports: - - containerPort: 8080 - env: - - name: PORT - value: "8080" - - name: DAPR_HTTP_PORT - value: "3500" - - name: PUBSUB_NAME - value: "http-events" - - name: TOPIC_NAME +apiVersion: apps/v1 +kind: Deployment +metadata: + name: http-event-subscriber + labels: + app: http-event-subscriber + demo: http-event +spec: + selector: + matchLabels: + app: http-event-subscriber + template: + metadata: + labels: + app: http-event-subscriber + demo: http-event + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "http-event-subscriber" + dapr.io/app-port: "8080" + dapr.io/config: "tracing" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/http-event-subscriber:v0.11.1 + ports: + - containerPort: 8080 + env: + - name: PORT + value: "8080" + - name: DAPR_HTTP_PORT + value: "3500" + - name: PUBSUB_NAME + value: "http-events" + - name: TOPIC_NAME value: "messages" \ No newline at end of file diff --git a/http-event-subscriber/main.go b/http-event-subscriber/main.go index 728ea12..98e0d63 100644 --- a/http-event-subscriber/main.go +++ b/http-event-subscriber/main.go @@ -1,60 +1,60 @@ -package main - -import ( - "context" - "fmt" - "log" - - "net/http" - "os" - "strings" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" -) - -var ( - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8080") - pubSubName = getEnvVar("PUBSUB_NAME", "http-events") - topicName = getEnvVar("TOPIC_NAME", "messages") -) - -func main() { - // create a Dapr service - s := daprd.NewService(address) - - // add some topic subscriptions - subscription := &common.Subscription{ - PubsubName: pubSubName, - Topic: topicName, - Route: fmt.Sprintf("/%s", topicName), - } - - if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { - logger.Fatalf("error adding topic subscription: %v", err) - } - - // start the service - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("error starting service: %v", err) - } -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf( - "event - PubsubName:%s, Topic:%s, ID:%s, Data: %v", - e.PubsubName, e.Topic, e.ID, e.Data, - ) - - // TODO: do something with the cloud event data - - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "fmt" + "log" + + "net/http" + "os" + "strings" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" +) + +var ( + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8080") + pubSubName = getEnvVar("PUBSUB_NAME", "http-events") + topicName = getEnvVar("TOPIC_NAME", "messages") +) + +func main() { + // create a Dapr service + s := daprd.NewService(address) + + // add some topic subscriptions + subscription := &common.Subscription{ + PubsubName: pubSubName, + Topic: topicName, + Route: fmt.Sprintf("/%s", topicName), + } + + if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { + logger.Fatalf("error adding topic subscription: %v", err) + } + + // start the service + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("error starting service: %v", err) + } +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf( + "event - PubsubName:%s, Topic:%s, ID:%s, Data: %v", + e.PubsubName, e.Topic, e.ID, e.Data, + ) + + // TODO: do something with the cloud event data + + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/order-cancellation/README.md b/order-cancellation/README.md index 746748f..4cbd987 100644 --- a/order-cancellation/README.md +++ b/order-cancellation/README.md @@ -1,95 +1,95 @@ -# Dapr integrations demo - -> WIP: This demo is being currently updated to Dapr v1.0.0.rc-1 - -## Use-case - -Order cancellation demo, loosely based on [Shopify API](https://shopify.dev/docs/admin-api/rest/reference/orders/order?api[version]=2020-04) order cancellation use-case to showcase multiple Dapr service integrations in a single solution: - -![Use-case](img/usecase.png) - -## Component Overview - -* **Dapr API** endpoint published with JWT token auth in **Daprized Ngnx** ingress -* **Dapr Workflows** to orchestrate cancellation process using **Logic Apps** runtime -* **Dapr Functions** extensions to create and persist audit state into **Mongo DB** -* **Dapr Eventing** using **Redis Queue** for order message queue -* **Daprized Web App** as order processing dashboard -* **Dapr Binding** to send confirmation emails using **SendGrid** -* **Dapr Distributed Tracing** to capture and forward traces to **Zipkin** - -![Draft Demo Flow Diagram](img/diagram.png) - - -## Demo - -Dapr integration demo consists of: - -1. Starting the order processing dashboard -2. Submitting cancellation request -3. Viewing processed request in the dashboard -4. Querying the state store for cancellation data -5. Showing order cancellation confirmation email -6. Review of the distributed traces for entire process - -> Note, instructions on how to setup a Kubernetes cluster for this demo are located [here](../setup/README.md) - -### 1. Dashboard - -> Note, these instructions assume `demo.dapr.team` domain setup in the [cluster setup](../setup/README.md) step. You will need to substitute this for your own domain. - -Navigate to https://order.demo.dapr.team to start the order processing dashboard. There won't be any data yet, so this is just to open the WebSocket connection. - -![Initial UI](img/ui1.png) - -### 2. Submit Cancellation - -Submit the order [cancellation.json](demo/data/cancellation.json) file using `curl` - -```shell -API_TOKEN=$(kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode) -curl -i \ - -d @demo/data/cancellation.json \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.demo.dapr.team/v1.0/invoke/workflows.order/method/order-cancel" -``` - -### 3. Dashboard (updated) - -View the dashboard again at https://order.demo.dapr.team to see the orders - -### 4. Email - -Check configured email box for cancellation confirmation after the processed completed - -> Make sure to check junk mail! - -### 5. Observability - -Forward local ports: - -```shell -kubectl port-forward svc/kibana-kibana 5601 -n dapr-monitoring & -kubectl port-forward svc/grafana 8888:80 -n dapr-monitoring & -kubectl port-forward svc/zipkin 9411 -n dapr-monitoring & -``` - -#### Traces - -Navigate to Zipkin: http://localhost:9411 - -#### Logs - -Navigate to Kibana: http://localhost:5601 - -#### Metrics - -Navigate to Grafana: http://localhost:8888 - -## Setup - -For script through this demo see [setup](./demo). - - - +# Dapr integrations demo + +> WIP: This demo is being currently updated to Dapr v1.0.0.rc-1 + +## Use-case + +Order cancellation demo, loosely based on [Shopify API](https://shopify.dev/docs/admin-api/rest/reference/orders/order?api[version]=2020-04) order cancellation use-case to showcase multiple Dapr service integrations in a single solution: + +![Use-case](img/usecase.png) + +## Component Overview + +* **Dapr API** endpoint published with JWT token auth in **Daprized Ngnx** ingress +* **Dapr Workflows** to orchestrate cancellation process using **Logic Apps** runtime +* **Dapr Functions** extensions to create and persist audit state into **Mongo DB** +* **Dapr Eventing** using **Redis Queue** for order message queue +* **Daprized Web App** as order processing dashboard +* **Dapr Binding** to send confirmation emails using **SendGrid** +* **Dapr Distributed Tracing** to capture and forward traces to **Zipkin** + +![Draft Demo Flow Diagram](img/diagram.png) + + +## Demo + +Dapr integration demo consists of: + +1. Starting the order processing dashboard +2. Submitting cancellation request +3. Viewing processed request in the dashboard +4. Querying the state store for cancellation data +5. Showing order cancellation confirmation email +6. Review of the distributed traces for entire process + +> Note, instructions on how to setup a Kubernetes cluster for this demo are located [here](../setup/README.md) + +### 1. Dashboard + +> Note, these instructions assume `demo.dapr.team` domain setup in the [cluster setup](../setup/README.md) step. You will need to substitute this for your own domain. + +Navigate to https://order.demo.dapr.team to start the order processing dashboard. There won't be any data yet, so this is just to open the WebSocket connection. + +![Initial UI](img/ui1.png) + +### 2. Submit Cancellation + +Submit the order [cancellation.json](demo/data/cancellation.json) file using `curl` + +```shell +API_TOKEN=$(kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode) +curl -i \ + -d @demo/data/cancellation.json \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.demo.dapr.team/v1.0/invoke/workflows.order/method/order-cancel" +``` + +### 3. Dashboard (updated) + +View the dashboard again at https://order.demo.dapr.team to see the orders + +### 4. Email + +Check configured email box for cancellation confirmation after the processed completed + +> Make sure to check junk mail! + +### 5. Observability + +Forward local ports: + +```shell +kubectl port-forward svc/kibana-kibana 5601 -n dapr-monitoring & +kubectl port-forward svc/grafana 8888:80 -n dapr-monitoring & +kubectl port-forward svc/zipkin 9411 -n dapr-monitoring & +``` + +#### Traces + +Navigate to Zipkin: http://localhost:9411 + +#### Logs + +Navigate to Kibana: http://localhost:5601 + +#### Metrics + +Navigate to Grafana: http://localhost:8888 + +## Setup + +For script through this demo see [setup](./demo). + + + diff --git a/order-cancellation/demo/Makefile b/order-cancellation/demo/Makefile index dfec2b9..d1eee49 100644 --- a/order-cancellation/demo/Makefile +++ b/order-cancellation/demo/Makefile @@ -1,33 +1,33 @@ -.PHONY: help, workflowport, fnport, postmeta, postmail, postcancel, postgateway -all: help - -workflowport: ## Forwards Dapr Workflow locally - kubectl port-forward deployment/dapr-workflows-host 3500:3500 - -fnport: ## Forwards Dapr Functions locally - kubectl port-forward deployment/auditor 3500:3500 - -postmeta: ## Invokes cancel on the workflow - curl -v -d @data/meta.json -H "Content-type: application/json" \ - http://localhost:3500/v1.0/publish/cancellations - -postmail: ## Invokes email on the workflow - curl -v -d @data/email.json -H "Content-type: application/json" \ - http://localhost:3500/v1.0/bindings/email - -postcancel: ## Invokes cancel on the workflow - curl -v -d @data/cancellation.json \ - -H "Content-type: application/json" \ - http://localhost:3500/v1.0/invoke/workflows/method/order-cancel - -postgateway: ## Invokes cancel on the gateway - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -v -d @data/cancellation.json \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/publish/queue/processed" - - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +.PHONY: help, workflowport, fnport, postmeta, postmail, postcancel, postgateway +all: help + +workflowport: ## Forwards Dapr Workflow locally + kubectl port-forward deployment/dapr-workflows-host 3500:3500 + +fnport: ## Forwards Dapr Functions locally + kubectl port-forward deployment/auditor 3500:3500 + +postmeta: ## Invokes cancel on the workflow + curl -v -d @data/meta.json -H "Content-type: application/json" \ + http://localhost:3500/v1.0/publish/cancellations + +postmail: ## Invokes email on the workflow + curl -v -d @data/email.json -H "Content-type: application/json" \ + http://localhost:3500/v1.0/bindings/email + +postcancel: ## Invokes cancel on the workflow + curl -v -d @data/cancellation.json \ + -H "Content-type: application/json" \ + http://localhost:3500/v1.0/invoke/workflows/method/order-cancel + +postgateway: ## Invokes cancel on the gateway + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -v -d @data/cancellation.json \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/publish/queue/processed" + + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/order-cancellation/demo/README.md b/order-cancellation/demo/README.md index 691fb3d..113d6bf 100644 --- a/order-cancellation/demo/README.md +++ b/order-cancellation/demo/README.md @@ -1,128 +1,128 @@ -# Dapr integrations demo setup - -> Note, this setup assumes Kubernetes cluster created using the [demo cluster setup instructions](../../setup/) - -## Components - -Start by create the `order` namespace: - -```shell -kubectl apply -f ./deployment/space.yaml -``` - -SandGrid secret - -> the queue and store secrets were already created during the cluster setup - -```shell -kubectl create secret generic email --from-literal=api-key="${SENDGRID_KEY}" -n order -``` - -Now deploy the queue, state and workflow stores, and email components - -```shell -kubectl apply -f ./component -``` - -## Deployments - -### Auditor - -Deploy Dapr auditor functions and wait for it to be ready - -```shell -kubectl apply -f deployment/auditor.yaml -kubectl rollout status deployment/order-auditor -n order -``` - -Check logs for errors from both containers - -```shell -kubectl logs -l app=order-auditor -c daprd -n order --tail 300 -kubectl logs -l app=order-auditor -c auditor -n order -``` - -### Workflow - -Create the config map to hold the Dapr workflow definition - -```shell -kubectl create cm workflows --from-file config/order-cancel.json -n order -``` - -Create the Azure storage account - -```shell -az storage account create --name daprintdemo --sku Standard_LRS -export AZSAKEY=$(az storage account keys list --account-name daprintdemo --query "[0].value" --output tsv) -``` - -Create secret to hold the workflow Azure storage account key - -> TODO: Remove Azure storage account dependency - -```shell -kubectl create secret generic dapr-workflows \ - --from-literal=accountName=daprintdemo \ - --from-literal=accountKey=$AZSAKEY \ - -n order -``` - -Deploy Dapr Workflows host and wait for it to be ready - -```shell -kubectl apply -f deployment/workflow.yaml -kubectl rollout status deployment/workflows-host -``` - -Check logs for errors from both containers - -```shell -kubectl logs -l app=workflows-host -c daprd -n order --tail 300 -kubectl logs -l app=workflows-host -c host -n order -``` - -### Viewer - -Deploy Dashboard - -```shell -kubectl apply -f deployment/viewer.yaml -kubectl rollout status deployment/order-viewer -``` - -Create the TLS certs for this domain - -> `demo.dapr.team` is the domain I'm using for this demo - -```shell -kubectl create secret tls tls-secret \ - -n order \ - --key ../../setup/certs/demo.dapr.team/cert-pk.pem \ - --cert ../../setup/certs/demo.dapr.team/cert-ca.pem -``` - -Deploy ingress for `order` - -```shell -kubectl apply -f deployment/ingress.yaml -``` - - -Test it: https://order.demo.dapr.team - -## Cleanup - - -```shell -kubectl delete -f ./deployment -kubectl delete -f ./component - -kubectl delete secret email -n order -kubectl delete secret dapr-workflows -n order -kubectl delete configmap workflows -n order - -az storage account delete --name daprintdemo -``` - - +# Dapr integrations demo setup + +> Note, this setup assumes Kubernetes cluster created using the [demo cluster setup instructions](../../setup/) + +## Components + +Start by create the `order` namespace: + +```shell +kubectl apply -f ./deployment/space.yaml +``` + +SandGrid secret + +> the queue and store secrets were already created during the cluster setup + +```shell +kubectl create secret generic email --from-literal=api-key="${SENDGRID_KEY}" -n order +``` + +Now deploy the queue, state and workflow stores, and email components + +```shell +kubectl apply -f ./component +``` + +## Deployments + +### Auditor + +Deploy Dapr auditor functions and wait for it to be ready + +```shell +kubectl apply -f deployment/auditor.yaml +kubectl rollout status deployment/order-auditor -n order +``` + +Check logs for errors from both containers + +```shell +kubectl logs -l app=order-auditor -c daprd -n order --tail 300 +kubectl logs -l app=order-auditor -c auditor -n order +``` + +### Workflow + +Create the config map to hold the Dapr workflow definition + +```shell +kubectl create cm workflows --from-file config/order-cancel.json -n order +``` + +Create the Azure storage account + +```shell +az storage account create --name daprintdemo --sku Standard_LRS +export AZSAKEY=$(az storage account keys list --account-name daprintdemo --query "[0].value" --output tsv) +``` + +Create secret to hold the workflow Azure storage account key + +> TODO: Remove Azure storage account dependency + +```shell +kubectl create secret generic dapr-workflows \ + --from-literal=accountName=daprintdemo \ + --from-literal=accountKey=$AZSAKEY \ + -n order +``` + +Deploy Dapr Workflows host and wait for it to be ready + +```shell +kubectl apply -f deployment/workflow.yaml +kubectl rollout status deployment/workflows-host +``` + +Check logs for errors from both containers + +```shell +kubectl logs -l app=workflows-host -c daprd -n order --tail 300 +kubectl logs -l app=workflows-host -c host -n order +``` + +### Viewer + +Deploy Dashboard + +```shell +kubectl apply -f deployment/viewer.yaml +kubectl rollout status deployment/order-viewer +``` + +Create the TLS certs for this domain + +> `demo.dapr.team` is the domain I'm using for this demo + +```shell +kubectl create secret tls tls-secret \ + -n order \ + --key ../../setup/certs/demo.dapr.team/cert-pk.pem \ + --cert ../../setup/certs/demo.dapr.team/cert-ca.pem +``` + +Deploy ingress for `order` + +```shell +kubectl apply -f deployment/ingress.yaml +``` + + +Test it: https://order.demo.dapr.team + +## Cleanup + + +```shell +kubectl delete -f ./deployment +kubectl delete -f ./component + +kubectl delete secret email -n order +kubectl delete secret dapr-workflows -n order +kubectl delete configmap workflows -n order + +az storage account delete --name daprintdemo +``` + + diff --git a/order-cancellation/demo/component/audit-store.yaml b/order-cancellation/demo/component/audit-store.yaml index b83dee3..bc18ef2 100644 --- a/order-cancellation/demo/component/audit-store.yaml +++ b/order-cancellation/demo/component/audit-store.yaml @@ -1,22 +1,22 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: order-audit-store - namespace: order -spec: - type: state.mongodb - metadata: - - name: host - value: mongo-mongodb-headless.mongo.svc.cluster.local:27017 - - name: username - value: dapr - - name: password - secretKeyRef: - name: mongo-secret - key: password - - name: databaseName - value: dapr - - name: collectionName - value: audit -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: order-audit-store + namespace: order +spec: + type: state.mongodb + metadata: + - name: host + value: mongo-mongodb-headless.mongo.svc.cluster.local:27017 + - name: username + value: dapr + - name: password + secretKeyRef: + name: mongo-secret + key: password + - name: databaseName + value: dapr + - name: collectionName + value: audit +scopes: - order-auditor \ No newline at end of file diff --git a/order-cancellation/demo/component/email.yaml b/order-cancellation/demo/component/email.yaml index c6b156b..b5b7edb 100644 --- a/order-cancellation/demo/component/email.yaml +++ b/order-cancellation/demo/component/email.yaml @@ -1,16 +1,16 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: order-email - namespace: order -spec: - type: bindings.twilio.sendgrid - metadata: - - name: emailFrom - value: "demo@thingz.io" - - name: apiKey - secretKeyRef: - name: email - key: api-key -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: order-email + namespace: order +spec: + type: bindings.twilio.sendgrid + metadata: + - name: emailFrom + value: "demo@thingz.io" + - name: apiKey + secretKeyRef: + name: email + key: api-key +scopes: - workflows \ No newline at end of file diff --git a/order-cancellation/demo/component/queue.yaml b/order-cancellation/demo/component/queue.yaml index 86ec9f6..4434e3a 100644 --- a/order-cancellation/demo/component/queue.yaml +++ b/order-cancellation/demo/component/queue.yaml @@ -1,18 +1,18 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: order-queue - namespace: order -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password -scopes: -- order-auditor -- workflows +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: order-queue + namespace: order +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password +scopes: +- order-auditor +- workflows - order-viewer \ No newline at end of file diff --git a/order-cancellation/demo/component/workflow-store.yaml b/order-cancellation/demo/component/workflow-store.yaml index adfd0e3..ffe85df 100644 --- a/order-cancellation/demo/component/workflow-store.yaml +++ b/order-cancellation/demo/component/workflow-store.yaml @@ -1,22 +1,22 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: order-store - namespace: order -spec: - type: state.mongodb - metadata: - - name: host - value: mongo-mongodb-headless.mongo.svc.cluster.local:27017 - - name: username - value: dapr - - name: password - secretKeyRef: - name: mongo-secret - key: password - - name: databaseName - value: dapr - - name: collectionName - value: record -scopes: -- workflows +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: order-store + namespace: order +spec: + type: state.mongodb + metadata: + - name: host + value: mongo-mongodb-headless.mongo.svc.cluster.local:27017 + - name: username + value: dapr + - name: password + secretKeyRef: + name: mongo-secret + key: password + - name: databaseName + value: dapr + - name: collectionName + value: record +scopes: +- workflows diff --git a/order-cancellation/demo/config/order-cancel.json b/order-cancellation/demo/config/order-cancel.json index 7d3c490..50d87cd 100644 --- a/order-cancellation/demo/config/order-cancel.json +++ b/order-cancellation/demo/config/order-cancel.json @@ -1,111 +1,111 @@ -{ - "definition": { - "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", - "actions": { - "PublishCancellationsTopic": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "http://localhost:3500/v1.0/publish/order-queue/cancellations", - "body": { - "id": "@triggerBody().id", - "note": "@triggerBody().note", - "submitted_on": "@triggerBody().submitted_on" - }, - "headers": { - "Content-Type": "application/json" - } - }, - "runAfter": {} - }, - "SaveState": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "http://localhost:3500/v1.0/state/order-store", - "body": [ - { - "key": "@triggerBody().id", - "value": "@triggerBody()" - } - ], - "headers": { - "Content-Type": "application/json" - } - }, - "runAfter": { - "PublishCancellationsTopic": [ - "Succeeded" - ] - } - }, - "PublishProcessedTopic": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "http://localhost:3500/v1.0/publish/order-queue/processed", - "body": "@triggerBody()", - "headers": { - "Content-Type": "application/json" - } - }, - "runAfter": { - "SaveState": [ - "Succeeded" - ] - } - }, - "SendConfirmationEmail": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "http://localhost:3500/v1.0/bindings/order-email", - "body": { - "operation": "create", - "metadata": { - "emailTo": "mchmarny@microsoft.com", - "subject": "Order cancellation confirmation" - }, - "data": "

Order cancellation confirmed

Your order has been canceled.

Thank you and we hope you come back.

" - }, - "headers": { - "Content-Type": "application/json" - } - }, - "runAfter": { - "PublishProcessedTopic": [ - "Succeeded" - ] - } - }, - "Response": { - "inputs": { - "body": { - "id:": "@triggerBody().id", - "submitted_on": "@triggerBody().submitted_on", - "approved": true - }, - "statusCode": 200 - }, - "runAfter": { - "PublishProcessedTopic": [ - "Succeeded" - ] - }, - "type": "Response" - } - }, - "contentVersion": "1.0.0.0", - "outputs": {}, - "parameters": {}, - "triggers": { - "manual": { - "inputs": { - "schema": {} - }, - "kind": "Http", - "type": "Request" - } - } - } +{ + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "actions": { + "PublishCancellationsTopic": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "http://localhost:3500/v1.0/publish/order-queue/cancellations", + "body": { + "id": "@triggerBody().id", + "note": "@triggerBody().note", + "submitted_on": "@triggerBody().submitted_on" + }, + "headers": { + "Content-Type": "application/json" + } + }, + "runAfter": {} + }, + "SaveState": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "http://localhost:3500/v1.0/state/order-store", + "body": [ + { + "key": "@triggerBody().id", + "value": "@triggerBody()" + } + ], + "headers": { + "Content-Type": "application/json" + } + }, + "runAfter": { + "PublishCancellationsTopic": [ + "Succeeded" + ] + } + }, + "PublishProcessedTopic": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "http://localhost:3500/v1.0/publish/order-queue/processed", + "body": "@triggerBody()", + "headers": { + "Content-Type": "application/json" + } + }, + "runAfter": { + "SaveState": [ + "Succeeded" + ] + } + }, + "SendConfirmationEmail": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "http://localhost:3500/v1.0/bindings/order-email", + "body": { + "operation": "create", + "metadata": { + "emailTo": "mchmarny@microsoft.com", + "subject": "Order cancellation confirmation" + }, + "data": "

Order cancellation confirmed

Your order has been canceled.

Thank you and we hope you come back.

" + }, + "headers": { + "Content-Type": "application/json" + } + }, + "runAfter": { + "PublishProcessedTopic": [ + "Succeeded" + ] + } + }, + "Response": { + "inputs": { + "body": { + "id:": "@triggerBody().id", + "submitted_on": "@triggerBody().submitted_on", + "approved": true + }, + "statusCode": 200 + }, + "runAfter": { + "PublishProcessedTopic": [ + "Succeeded" + ] + }, + "type": "Response" + } + }, + "contentVersion": "1.0.0.0", + "outputs": {}, + "parameters": {}, + "triggers": { + "manual": { + "inputs": { + "schema": {} + }, + "kind": "Http", + "type": "Request" + } + } + } } \ No newline at end of file diff --git a/order-cancellation/demo/data/cancellation.json b/order-cancellation/demo/data/cancellation.json index ad0ef61..5cb4eb4 100644 --- a/order-cancellation/demo/data/cancellation.json +++ b/order-cancellation/demo/data/cancellation.json @@ -1,32 +1,32 @@ -{ - "id": "f727735e-b64e-11ea-b3de-0242ac130004", - "note": "Customer made a mistake", - "submitted_on": "2020-06-24T13:02:15Z", - "refund": { - "shipping": { - "full_refund": true - }, - "refund_line_items": [ - { - "line_item_id": 466157049, - "quantity": 1, - "restock_type": "cancel", - "location_id": 48752903 - } - ], - "transactions": [ - { - "parent_id": 1072844675, - "amount": "10.00", - "kind": "refund", - "gateway": "bogus" - }, - { - "parent_id": 1072844676, - "amount": "100.00", - "kind": "refund", - "gateway": "gift_card" - } - ] - } +{ + "id": "f727735e-b64e-11ea-b3de-0242ac130004", + "note": "Customer made a mistake", + "submitted_on": "2020-06-24T13:02:15Z", + "refund": { + "shipping": { + "full_refund": true + }, + "refund_line_items": [ + { + "line_item_id": 466157049, + "quantity": 1, + "restock_type": "cancel", + "location_id": 48752903 + } + ], + "transactions": [ + { + "parent_id": 1072844675, + "amount": "10.00", + "kind": "refund", + "gateway": "bogus" + }, + { + "parent_id": 1072844676, + "amount": "100.00", + "kind": "refund", + "gateway": "gift_card" + } + ] + } } \ No newline at end of file diff --git a/order-cancellation/demo/data/email.json b/order-cancellation/demo/data/email.json index e4cdd01..b840003 100644 --- a/order-cancellation/demo/data/email.json +++ b/order-cancellation/demo/data/email.json @@ -1,8 +1,8 @@ -{ - "operation": "create", - "metadata": { - "emailTo": "mchmarny@microsoft.com", - "subject": "Order cancellation confirmation" - }, - "data": "

Order cancellation confirmed

Your order from 2020-06-24T13:02:15Z has been canceled.

Thank you and we hope you come back.

Order: b76b547e-b64e-11ea-b3de-0242ac130004

" +{ + "operation": "create", + "metadata": { + "emailTo": "mchmarny@microsoft.com", + "subject": "Order cancellation confirmation" + }, + "data": "

Order cancellation confirmed

Your order from 2020-06-24T13:02:15Z has been canceled.

Thank you and we hope you come back.

Order: b76b547e-b64e-11ea-b3de-0242ac130004

" } \ No newline at end of file diff --git a/order-cancellation/demo/data/meta.json b/order-cancellation/demo/data/meta.json index 0b406f1..12a1967 100644 --- a/order-cancellation/demo/data/meta.json +++ b/order-cancellation/demo/data/meta.json @@ -1,5 +1,5 @@ -{ - "id": "b76b547e-b64e-11ea-b3de-0242ac130004", - "note": "Customer made a mistake", - "submitted_on": "2020-06-24T13:02:15Z" +{ + "id": "b76b547e-b64e-11ea-b3de-0242ac130004", + "note": "Customer made a mistake", + "submitted_on": "2020-06-24T13:02:15Z" } \ No newline at end of file diff --git a/order-cancellation/demo/deployment/auditor.yaml b/order-cancellation/demo/deployment/auditor.yaml index 8e9bf9d..c5d5dd8 100644 --- a/order-cancellation/demo/deployment/auditor.yaml +++ b/order-cancellation/demo/deployment/auditor.yaml @@ -1,38 +1,38 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: order-auditor - namespace: order - labels: - app: order-auditor -spec: - replicas: 1 - selector: - matchLabels: - app: order-auditor - template: - metadata: - labels: - app: order-auditor - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "order-auditor" - dapr.io/app-protocol: "http" - dapr.io/app-port: "3001" - dapr.io/config: "order-config" - dapr.io/log-as-json: "true" - spec: - containers: - - name: auditor - image: mchmarny/auditor:v0.2.10 - ports: - - containerPort: 3001 - env: - - name: StateStore - value: "order-audit-store" - - name: StateKey - value: "id" - - name: PubSubName - value: "order-queue" - - name: TopicName +apiVersion: apps/v1 +kind: Deployment +metadata: + name: order-auditor + namespace: order + labels: + app: order-auditor +spec: + replicas: 1 + selector: + matchLabels: + app: order-auditor + template: + metadata: + labels: + app: order-auditor + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "order-auditor" + dapr.io/app-protocol: "http" + dapr.io/app-port: "3001" + dapr.io/config: "order-config" + dapr.io/log-as-json: "true" + spec: + containers: + - name: auditor + image: mchmarny/auditor:v0.2.10 + ports: + - containerPort: 3001 + env: + - name: StateStore + value: "order-audit-store" + - name: StateKey + value: "id" + - name: PubSubName + value: "order-queue" + - name: TopicName value: "cancellations" \ No newline at end of file diff --git a/order-cancellation/demo/deployment/ingress.yaml b/order-cancellation/demo/deployment/ingress.yaml index 6b02788..221db74 100644 --- a/order-cancellation/demo/deployment/ingress.yaml +++ b/order-cancellation/demo/deployment/ingress.yaml @@ -1,21 +1,21 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: order-ingress-rules - namespace: order - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/rewrite-target: / -spec: - tls: - - hosts: - - order.demo.dapr.team - secretName: tls-secret - rules: - - host: order.demo.dapr.team - http: - paths: - - path: / - backend: - serviceName: order-viewer +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: order-ingress-rules + namespace: order + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + tls: + - hosts: + - order.demo.dapr.team + secretName: tls-secret + rules: + - host: order.demo.dapr.team + http: + paths: + - path: / + backend: + serviceName: order-viewer servicePort: 80 \ No newline at end of file diff --git a/order-cancellation/demo/deployment/space.yaml b/order-cancellation/demo/deployment/space.yaml index 79f4f01..df4644c 100644 --- a/order-cancellation/demo/deployment/space.yaml +++ b/order-cancellation/demo/deployment/space.yaml @@ -1,49 +1,49 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: order ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: order-config - namespace: order -spec: - tracing: - samplingRate: "1" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: secret-reader - namespace: order -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: dapr-secret-reader - namespace: order -subjects: -- kind: ServiceAccount - name: default -roleRef: - kind: Role - name: secret-reader - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: zipkin - namespace: order -spec: - type: exporters.zipkin - metadata: - - name: enabled - value: "true" - - name: exporterAddress - value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" +apiVersion: v1 +kind: Namespace +metadata: + name: order +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: order-config + namespace: order +spec: + tracing: + samplingRate: "1" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secret-reader + namespace: order +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dapr-secret-reader + namespace: order +subjects: +- kind: ServiceAccount + name: default +roleRef: + kind: Role + name: secret-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: zipkin + namespace: order +spec: + type: exporters.zipkin + metadata: + - name: enabled + value: "true" + - name: exporterAddress + value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" diff --git a/order-cancellation/demo/deployment/viewer.yaml b/order-cancellation/demo/deployment/viewer.yaml index 23bef98..32842d0 100644 --- a/order-cancellation/demo/deployment/viewer.yaml +++ b/order-cancellation/demo/deployment/viewer.yaml @@ -1,50 +1,50 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: order-viewer - namespace: order - labels: - app: order-viewer -spec: - replicas: 1 - selector: - matchLabels: - app: order-viewer - template: - metadata: - labels: - app: order-viewer - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "order-viewer" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8080" - dapr.io/config: "order-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: viewer - image: mchmarny/order-viewer:v0.11.1 - ports: - - containerPort: 8080 - env: - - name: ADDRESS - value: ":8080" - - name: PUBSUB_NAME - value: order-queue - - name: TOPIC_NAME - value: processed ---- -apiVersion: v1 -kind: Service -metadata: - name: order-viewer - namespace: order -spec: - selector: - app: order-viewer - ports: - - protocol: TCP - port: 80 +apiVersion: apps/v1 +kind: Deployment +metadata: + name: order-viewer + namespace: order + labels: + app: order-viewer +spec: + replicas: 1 + selector: + matchLabels: + app: order-viewer + template: + metadata: + labels: + app: order-viewer + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "order-viewer" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8080" + dapr.io/config: "order-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: viewer + image: mchmarny/order-viewer:v0.11.1 + ports: + - containerPort: 8080 + env: + - name: ADDRESS + value: ":8080" + - name: PUBSUB_NAME + value: order-queue + - name: TOPIC_NAME + value: processed +--- +apiVersion: v1 +kind: Service +metadata: + name: order-viewer + namespace: order +spec: + selector: + app: order-viewer + ports: + - protocol: TCP + port: 80 targetPort: 8080 \ No newline at end of file diff --git a/order-cancellation/demo/deployment/workflow.yaml b/order-cancellation/demo/deployment/workflow.yaml index fae373d..169afe0 100644 --- a/order-cancellation/demo/deployment/workflow.yaml +++ b/order-cancellation/demo/deployment/workflow.yaml @@ -1,51 +1,51 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: workflows-host - namespace: order - labels: - app: workflows-host -spec: - replicas: 1 - selector: - matchLabels: - app: workflows-host - template: - metadata: - labels: - app: workflows-host - annotations: - dapr.io/app-id: "workflows" - dapr.io/enabled: "true" - dapr.io/app-port: "50003" - dapr.io/app-protocol: "grpc" - dapr.io/config: "order-config" - dapr.io/log-level: "debug" - dapr.io/log-as-json: "true" - spec: - containers: - - name: host - image: daprio/workflows:0.2.2 - env: - - name: STORAGE_ACCOUNT_KEY - valueFrom: - secretKeyRef: - name: dapr-workflows - key: accountKey - - name: STORAGE_ACCOUNT_NAME - valueFrom: - secretKeyRef: - name: dapr-workflows - key: accountName - command: ["dotnet"] - args: ["app/Dapr.Workflows.dll", "--workflows-path", "/workflows"] - ports: - - containerPort: 50003 - imagePullPolicy: Always - volumeMounts: - - name: workflows - mountPath: /workflows - volumes: - - name: workflows - configMap: +apiVersion: apps/v1 +kind: Deployment +metadata: + name: workflows-host + namespace: order + labels: + app: workflows-host +spec: + replicas: 1 + selector: + matchLabels: + app: workflows-host + template: + metadata: + labels: + app: workflows-host + annotations: + dapr.io/app-id: "workflows" + dapr.io/enabled: "true" + dapr.io/app-port: "50003" + dapr.io/app-protocol: "grpc" + dapr.io/config: "order-config" + dapr.io/log-level: "debug" + dapr.io/log-as-json: "true" + spec: + containers: + - name: host + image: daprio/workflows:0.2.2 + env: + - name: STORAGE_ACCOUNT_KEY + valueFrom: + secretKeyRef: + name: dapr-workflows + key: accountKey + - name: STORAGE_ACCOUNT_NAME + valueFrom: + secretKeyRef: + name: dapr-workflows + key: accountName + command: ["dotnet"] + args: ["app/Dapr.Workflows.dll", "--workflows-path", "/workflows"] + ports: + - containerPort: 50003 + imagePullPolicy: Always + volumeMounts: + - name: workflows + mountPath: /workflows + volumes: + - name: workflows + configMap: name: workflows \ No newline at end of file diff --git a/order-cancellation/src/fn/.gitignore b/order-cancellation/src/fn/.gitignore index 3c5b2f1..b753bdf 100644 --- a/order-cancellation/src/fn/.gitignore +++ b/order-cancellation/src/fn/.gitignore @@ -1,7 +1,7 @@ -# Build generated files in src -/objd/**/* -/obj/**/* -/bin/**/* -/gen/**/* -/.vs/**/* -/.vscode/**/* +# Build generated files in src +/objd/**/* +/obj/**/* +/bin/**/* +/gen/**/* +/.vs/**/* +/.vscode/**/* diff --git a/order-cancellation/src/fn/DaprAzFn.csproj b/order-cancellation/src/fn/DaprAzFn.csproj index 91308f7..e43e9bd 100644 --- a/order-cancellation/src/fn/DaprAzFn.csproj +++ b/order-cancellation/src/fn/DaprAzFn.csproj @@ -1,23 +1,23 @@ - - - netcoreapp3.0 - v3 - DaprAzFn - - - - - - - - - - - PreserveNewest - - - PreserveNewest - Never - - - + + + netcoreapp3.0 + v3 + DaprAzFn + + + + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + diff --git a/order-cancellation/src/fn/Dockerfile b/order-cancellation/src/fn/Dockerfile index 9f5c1d2..f3fca49 100644 --- a/order-cancellation/src/fn/Dockerfile +++ b/order-cancellation/src/fn/Dockerfile @@ -1,14 +1,14 @@ -FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS installer-env - -COPY . /src/DaprAzFnSample -RUN cd /src/DaprAzFnSample && \ - mkdir -p /home/site/wwwroot && \ - dotnet publish *.csproj --output /home/site/wwwroot - -# To enable ssh & remote debugging on app service change the base image to the one below -# FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice -FROM mcr.microsoft.com/azure-functions/dotnet:3.0 -ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ - AzureFunctionsJobHost__Logging__Console__IsEnabled=true - +FROM mcr.microsoft.com/dotnet/core/sdk:3.0 AS installer-env + +COPY . /src/DaprAzFnSample +RUN cd /src/DaprAzFnSample && \ + mkdir -p /home/site/wwwroot && \ + dotnet publish *.csproj --output /home/site/wwwroot + +# To enable ssh & remote debugging on app service change the base image to the one below +# FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice +FROM mcr.microsoft.com/azure-functions/dotnet:3.0 +ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ + AzureFunctionsJobHost__Logging__Console__IsEnabled=true + COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"] \ No newline at end of file diff --git a/order-cancellation/src/fn/Makefile b/order-cancellation/src/fn/Makefile index 8a603f9..ab05d6a 100644 --- a/order-cancellation/src/fn/Makefile +++ b/order-cancellation/src/fn/Makefile @@ -1,33 +1,33 @@ -APP_NAME =auditor -RELEASE_VERSION =v0.2.10 -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: test mod run dapr lint image tag clean help -all: test - -restore: ## Restores the project - dotnet restore - -run: ## Runs function locally - dapr run --app-id fn \ - --app-port 3001 \ - --port 3501 \ - --components-path config \ - env PubSubName=queue TopicName=cancellations StateStore=store func host start - -event: ## Publishes event onto the Dapr topic - curl -d @./config/meta.json \ - -H "Content-type: application/json" \ - http://localhost:3501/v1.0/publish/queue/cancellations - -test: restore ## Tests the entire project - dotnet test - -image: restore ## Builds docker image and publishes it to Dockerhub - docker build -t $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) . - docker push $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) - - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +APP_NAME =auditor +RELEASE_VERSION =v0.2.10 +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: test mod run dapr lint image tag clean help +all: test + +restore: ## Restores the project + dotnet restore + +run: ## Runs function locally + dapr run --app-id fn \ + --app-port 3001 \ + --port 3501 \ + --components-path config \ + env PubSubName=queue TopicName=cancellations StateStore=store func host start + +event: ## Publishes event onto the Dapr topic + curl -d @./config/meta.json \ + -H "Content-type: application/json" \ + http://localhost:3501/v1.0/publish/queue/cancellations + +test: restore ## Tests the entire project + dotnet test + +image: restore ## Builds docker image and publishes it to Dockerhub + docker build -t $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) . + docker push $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) + + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/order-cancellation/src/fn/ReceiveEvent.cs b/order-cancellation/src/fn/ReceiveEvent.cs index 4acdf6d..1af8aef 100644 --- a/order-cancellation/src/fn/ReceiveEvent.cs +++ b/order-cancellation/src/fn/ReceiveEvent.cs @@ -1,44 +1,44 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -// ------------------------------------------------------------ - -namespace DaprAzFn.Sample -{ - using System; - using System.IO; - using System.Threading.Tasks; - using Microsoft.AspNetCore.Mvc; - using CloudNative.CloudEvents; - using Microsoft.Azure.WebJobs; - using Dapr.AzureFunctions.Extension; - using Microsoft.Azure.WebJobs.Extensions.Http; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.Logging; - using Newtonsoft.Json.Linq; - - public static class ReceiveTopicMessage - { - /// - /// Subscribes to topic and saves it to state store - /// - [FunctionName("ReceiveTopicMessage")] - public static async Task Run( - [DaprTopicTrigger("%PubSubName%", Topic = "%TopicName%")] CloudEvent cloudEvent, - [DaprState("%StateStore%")] IAsyncCollector state, ILogger log) - { - // Get data from CloudEvent - log.LogInformation($"Received message: {cloudEvent.Data}."); - var cancellationData = cloudEvent.Data as JToken; - - // TODO: Implement cancellation validaiton logic. - - // Parse state ID and Save - var keyname = Environment.GetEnvironmentVariable("StateKey") ?? "id"; - var key = cancellationData.Value(keyname); - var stateRec = new DaprStateRecord(key, cancellationData); - await state.AddAsync(stateRec); - return new OkResult(); - } - } -} +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +namespace DaprAzFn.Sample +{ + using System; + using System.IO; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Mvc; + using CloudNative.CloudEvents; + using Microsoft.Azure.WebJobs; + using Dapr.AzureFunctions.Extension; + using Microsoft.Azure.WebJobs.Extensions.Http; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Logging; + using Newtonsoft.Json.Linq; + + public static class ReceiveTopicMessage + { + /// + /// Subscribes to topic and saves it to state store + /// + [FunctionName("ReceiveTopicMessage")] + public static async Task Run( + [DaprTopicTrigger("%PubSubName%", Topic = "%TopicName%")] CloudEvent cloudEvent, + [DaprState("%StateStore%")] IAsyncCollector state, ILogger log) + { + // Get data from CloudEvent + log.LogInformation($"Received message: {cloudEvent.Data}."); + var cancellationData = cloudEvent.Data as JToken; + + // TODO: Implement cancellation validaiton logic. + + // Parse state ID and Save + var keyname = Environment.GetEnvironmentVariable("StateKey") ?? "id"; + var key = cancellationData.Value(keyname); + var stateRec = new DaprStateRecord(key, cancellationData); + await state.AddAsync(stateRec); + return new OkResult(); + } + } +} diff --git a/order-cancellation/src/fn/config/meta.json b/order-cancellation/src/fn/config/meta.json index 0b406f1..12a1967 100644 --- a/order-cancellation/src/fn/config/meta.json +++ b/order-cancellation/src/fn/config/meta.json @@ -1,5 +1,5 @@ -{ - "id": "b76b547e-b64e-11ea-b3de-0242ac130004", - "note": "Customer made a mistake", - "submitted_on": "2020-06-24T13:02:15Z" +{ + "id": "b76b547e-b64e-11ea-b3de-0242ac130004", + "note": "Customer made a mistake", + "submitted_on": "2020-06-24T13:02:15Z" } \ No newline at end of file diff --git a/order-cancellation/src/fn/config/pubsub.yaml b/order-cancellation/src/fn/config/pubsub.yaml index 93d6f5a..640576d 100644 --- a/order-cancellation/src/fn/config/pubsub.yaml +++ b/order-cancellation/src/fn/config/pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: queue -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: queue +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/order-cancellation/src/fn/config/statestore.yaml b/order-cancellation/src/fn/config/statestore.yaml index 4443e27..9ba2775 100644 --- a/order-cancellation/src/fn/config/statestore.yaml +++ b/order-cancellation/src/fn/config/statestore.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: store -spec: - type: state.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: store +spec: + type: state.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/order-cancellation/src/fn/host.json b/order-cancellation/src/fn/host.json index b9f92c0..f08505a 100644 --- a/order-cancellation/src/fn/host.json +++ b/order-cancellation/src/fn/host.json @@ -1,3 +1,3 @@ -{ - "version": "2.0" +{ + "version": "2.0" } \ No newline at end of file diff --git a/order-cancellation/src/fn/local.settings.json b/order-cancellation/src/fn/local.settings.json index 8155294..40d6303 100644 --- a/order-cancellation/src/fn/local.settings.json +++ b/order-cancellation/src/fn/local.settings.json @@ -1,10 +1,10 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "FUNCTIONS_WORKER_RUNTIME": "dotnet", - "StateStore": "statestore", - "StateKey": "id", - "TopicName": "cancellations" - } +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet", + "StateStore": "statestore", + "StateKey": "id", + "TopicName": "cancellations" + } } \ No newline at end of file diff --git a/order-cancellation/src/viewer/Dockerfile b/order-cancellation/src/viewer/Dockerfile index 8f5bbf4..d284ea3 100755 --- a/order-cancellation/src/viewer/Dockerfile +++ b/order-cancellation/src/viewer/Dockerfile @@ -1,20 +1,20 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ARG RELEASE_VERSION=v0.0.1-default - -ENV RELEASE_VERSION=$RELEASE_VERSION -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -ldflags \ - "-w -extldflags '-static' -X main.AppVersion=${RELEASE_VERSION}" \ - -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . -COPY --from=builder /src/resource ./resource/ - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ARG RELEASE_VERSION=v0.0.1-default + +ENV RELEASE_VERSION=$RELEASE_VERSION +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -ldflags \ + "-w -extldflags '-static' -X main.AppVersion=${RELEASE_VERSION}" \ + -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . +COPY --from=builder /src/resource ./resource/ + +ENTRYPOINT ["./service"] diff --git a/order-cancellation/src/viewer/Makefile b/order-cancellation/src/viewer/Makefile index 6ceb544..3e74ffe 100644 --- a/order-cancellation/src/viewer/Makefile +++ b/order-cancellation/src/viewer/Makefile @@ -1,51 +1,51 @@ -APP_NAME =order-viewer -RELEASE_VERSION =v0.11.1 -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: test mod run dapr lint image tag clean help -all: test - -tidy: ## Updates the go modules and vendors - go mod tidy - go mod vendor - -test: mod ## Tests the entire project - go test -v -count=1 -race ./... - # go test -v -count=1 -run NameOfSingleTest ./... - -run: mod ## Runs the uncompiled code by itsef - go run -v main.go - -build: mod ## Build app binary locally - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -ldflags \ - "-w -extldflags '-static' -X main.AppVersion=$(RELEASE_VERSION)" \ - -mod vendor -o ./bin/service . - -dapr: mod ## Runs the uncompiled code in Dapr - dapr run --app-id viewer \ - --app-port 8083 \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go - -image: mod ## Builds docker image and publishes it to Dockerhub - docker build --build-arg APP_VERSION=$(RELEASE_VERSION) \ - -t $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) . - docker push $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans go and generated files in ./dapr/proto/ - go clean - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +APP_NAME =order-viewer +RELEASE_VERSION =v0.11.1 +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: test mod run dapr lint image tag clean help +all: test + +tidy: ## Updates the go modules and vendors + go mod tidy + go mod vendor + +test: mod ## Tests the entire project + go test -v -count=1 -race ./... + # go test -v -count=1 -run NameOfSingleTest ./... + +run: mod ## Runs the uncompiled code by itsef + go run -v main.go + +build: mod ## Build app binary locally + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -ldflags \ + "-w -extldflags '-static' -X main.AppVersion=$(RELEASE_VERSION)" \ + -mod vendor -o ./bin/service . + +dapr: mod ## Runs the uncompiled code in Dapr + dapr run --app-id viewer \ + --app-port 8083 \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go + +image: mod ## Builds docker image and publishes it to Dockerhub + docker build --build-arg APP_VERSION=$(RELEASE_VERSION) \ + -t $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) . + docker push $(DOCKER_USERNAME)/$(APP_NAME):$(RELEASE_VERSION) + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans go and generated files in ./dapr/proto/ + go clean + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/order-cancellation/src/viewer/config/queue.yaml b/order-cancellation/src/viewer/config/queue.yaml index b9c328c..b3e8ce6 100644 --- a/order-cancellation/src/viewer/config/queue.yaml +++ b/order-cancellation/src/viewer/config/queue.yaml @@ -1,9 +1,9 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: queue -spec: - type: pubsub.azure.servicebus - metadata: - - name: connectionString - value: Endpoint=sb://daprdemo.servicebus.windows.net/;SharedAccessKeyName=view;SharedAccessKey=RbTBiNnoj7wfxHqG8XeHWYn3sL4VFbdBpn0cTNi3Hyo=;EntityPath=processed +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: queue +spec: + type: pubsub.azure.servicebus + metadata: + - name: connectionString + value: Endpoint=sb://daprdemo.servicebus.windows.net/;SharedAccessKeyName=view;SharedAccessKey=RbTBiNnoj7wfxHqG8XeHWYn3sL4VFbdBpn0cTNi3Hyo=;EntityPath=processed diff --git a/order-cancellation/src/viewer/go.mod b/order-cancellation/src/viewer/go.mod index 76ffc2a..1264952 100644 --- a/order-cancellation/src/viewer/go.mod +++ b/order-cancellation/src/viewer/go.mod @@ -1,10 +1,10 @@ -module github.com/mchmarny/dapr-demos/order-cancellation/src/viewer - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/gorilla/websocket v1.4.2 // indirect - github.com/pkg/errors v0.9.1 - gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 -) +module github.com/mchmarny/dapr-demos/order-cancellation/src/viewer + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/gorilla/websocket v1.4.2 // indirect + github.com/pkg/errors v0.9.1 + gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 +) diff --git a/order-cancellation/src/viewer/go.sum b/order-cancellation/src/viewer/go.sum index 06a74bf..7003161 100644 --- a/order-cancellation/src/viewer/go.sum +++ b/order-cancellation/src/viewer/go.sum @@ -1,101 +1,101 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJcorb+a0hJIQ6uakU5gybjfLWHlXo= -gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJcorb+a0hJIQ6uakU5gybjfLWHlXo= +gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/order-cancellation/src/viewer/main.go b/order-cancellation/src/viewer/main.go index 00cb3c8..6c96674 100755 --- a/order-cancellation/src/viewer/main.go +++ b/order-cancellation/src/viewer/main.go @@ -1,121 +1,121 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "html/template" - "log" - "net/http" - "os" - "strings" - - "gopkg.in/olahol/melody.v1" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" - "github.com/pkg/errors" -) - -var ( - // AppVersion will be overritten during build - AppVersion = "v0.0.1-default" - - // service - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8083") - pubSubName = getEnvVar("PUBSUB_NAME", "queue") - topicName = getEnvVar("TOPIC_NAME", "processed") - - broadcaster *melody.Melody - templates *template.Template -) - -func main() { - - // server mux - mux := http.NewServeMux() - - // static content - mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("resource/static")))) - mux.HandleFunc("/favicon.ico", faviconHandler) - - // tempalates - templates = template.Must(template.ParseGlob("resource/template/*")) - - // websocket upgrade - broadcaster = melody.New() - broadcaster.Upgrader.CheckOrigin = func(r *http.Request) bool { return true } - - // other handlers - mux.HandleFunc("/", rootHandler) - mux.HandleFunc("/ws", wsHandler) - - // create a Dapr service - s := daprd.NewServiceWithMux(address, mux) - - // add some topic subscriptions - subscription := &common.Subscription{ - PubsubName: pubSubName, - Topic: topicName, - Route: fmt.Sprintf("/%s", topicName), - } - - if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { - logger.Fatalf("error adding topic subscription: %v", err) - } - - // start the service - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("error starting service: %v", err) - } -} - -func wsHandler(w http.ResponseWriter, r *http.Request) { - broadcaster.HandleRequest(w, r) -} - -func rootHandler(w http.ResponseWriter, r *http.Request) { - proto := r.Header.Get("x-forwarded-proto") - if proto == "" { - proto = "http" - } - - data := map[string]string{ - "host": r.Host, - "proto": proto, - "version": AppVersion, - } - - err := templates.ExecuteTemplate(w, "index", data) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func faviconHandler(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, "./resource/static/img/favicon.ico") -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf( - "event - PubsubName:%s, Topic:%s, ID:%s, Data: %v", - e.PubsubName, e.Topic, e.ID, e.Data, - ) - - b, err := json.Marshal(e.Data) - if err != nil { - return false, errors.Wrap(err, "error marshaling data") - } - - broadcaster.Broadcast(b) - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "fmt" + "html/template" + "log" + "net/http" + "os" + "strings" + + "gopkg.in/olahol/melody.v1" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" + "github.com/pkg/errors" +) + +var ( + // AppVersion will be overritten during build + AppVersion = "v0.0.1-default" + + // service + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8083") + pubSubName = getEnvVar("PUBSUB_NAME", "queue") + topicName = getEnvVar("TOPIC_NAME", "processed") + + broadcaster *melody.Melody + templates *template.Template +) + +func main() { + + // server mux + mux := http.NewServeMux() + + // static content + mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("resource/static")))) + mux.HandleFunc("/favicon.ico", faviconHandler) + + // tempalates + templates = template.Must(template.ParseGlob("resource/template/*")) + + // websocket upgrade + broadcaster = melody.New() + broadcaster.Upgrader.CheckOrigin = func(r *http.Request) bool { return true } + + // other handlers + mux.HandleFunc("/", rootHandler) + mux.HandleFunc("/ws", wsHandler) + + // create a Dapr service + s := daprd.NewServiceWithMux(address, mux) + + // add some topic subscriptions + subscription := &common.Subscription{ + PubsubName: pubSubName, + Topic: topicName, + Route: fmt.Sprintf("/%s", topicName), + } + + if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { + logger.Fatalf("error adding topic subscription: %v", err) + } + + // start the service + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("error starting service: %v", err) + } +} + +func wsHandler(w http.ResponseWriter, r *http.Request) { + broadcaster.HandleRequest(w, r) +} + +func rootHandler(w http.ResponseWriter, r *http.Request) { + proto := r.Header.Get("x-forwarded-proto") + if proto == "" { + proto = "http" + } + + data := map[string]string{ + "host": r.Host, + "proto": proto, + "version": AppVersion, + } + + err := templates.ExecuteTemplate(w, "index", data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func faviconHandler(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "./resource/static/img/favicon.ico") +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf( + "event - PubsubName:%s, Topic:%s, ID:%s, Data: %v", + e.PubsubName, e.Topic, e.ID, e.Data, + ) + + b, err := json.Marshal(e.Data) + if err != nil { + return false, errors.Wrap(err, "error marshaling data") + } + + broadcaster.Broadcast(b) + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/order-cancellation/src/viewer/resource/static/img/dapr.svg b/order-cancellation/src/viewer/resource/static/img/dapr.svg index 716c3e4..38d1cc0 100644 --- a/order-cancellation/src/viewer/resource/static/img/dapr.svg +++ b/order-cancellation/src/viewer/resource/static/img/dapr.svg @@ -1,15 +1,15 @@ - - - - Artboard - Created with Sketch. - - - - - - - - - + + + + Artboard + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/order-cancellation/src/viewer/resource/static/img/tw.svg b/order-cancellation/src/viewer/resource/static/img/tw.svg index 4d58e50..22cb07e 100644 --- a/order-cancellation/src/viewer/resource/static/img/tw.svg +++ b/order-cancellation/src/viewer/resource/static/img/tw.svg @@ -1,3 +1,3 @@ - - - - - - - +{{ define "footer" }} + + + + + + + + {{ end }} \ No newline at end of file diff --git a/order-cancellation/src/viewer/resource/template/header.html b/order-cancellation/src/viewer/resource/template/header.html index 7ee0a76..26147c6 100755 --- a/order-cancellation/src/viewer/resource/template/header.html +++ b/order-cancellation/src/viewer/resource/template/header.html @@ -1,28 +1,28 @@ -{{ define "header" }} - - - - - Dapr Event Viewer - - - - - - - - - - - -
- - - - +{{ define "header" }} + + + + + Dapr Event Viewer + + + + + + + + + + + +
+ + + + {{end}} \ No newline at end of file diff --git a/order-cancellation/src/viewer/resource/template/index.html b/order-cancellation/src/viewer/resource/template/index.html index fc0213c..6580154 100755 --- a/order-cancellation/src/viewer/resource/template/index.html +++ b/order-cancellation/src/viewer/resource/template/index.html @@ -1,25 +1,25 @@ -{{ define "index" }} - -{{ template "header" . }} - -
-
-
- - - -{{ template "footer" . }} - +{{ define "index" }} + +{{ template "header" . }} + +
+
+
+ + + +{{ template "footer" . }} + {{ end }} \ No newline at end of file diff --git a/pipeline/README.md b/pipeline/README.md index 56b9616..54a7cfc 100644 --- a/pipeline/README.md +++ b/pipeline/README.md @@ -1,307 +1,307 @@ -# Dapr pipeline demo - -Dapr supports a wide array of state and pubsub building blocks across multiple OSS, Cloud, and on-prem services. This demo shows how to use a few of these components to build tweet sentiment processing pipeline. - -![alt text](./img/overview.png "Pipeline Overview") - - -## Run in standalone mode - -### Setup - -To run these demos locally, you will have first create a secret file (`pipeline/secrets.json`). These will be used by Dapr components at runtime. To get the Twitter API secretes you will need to register your app [here](https://developer.twitter.com/en/apps/create). - -```json -{ - "Twitter": { - "ConsumerKey": "", - "ConsumerSecret": "", - "AccessToken": "", - "AccessSecret": "" - }, - "Azure": { - "CognitiveAPIKey": "" - } -} -``` - - -### Start tweet viewer app - -Navigate to the [tweet-viewer](./tweet-viewer) directory and run: - -```shell -cd tweet-viewer -dapr run \ - --app-id tweet-viewer \ - --app-port 8084 \ - --app-protocol http \ - --components-path ./config \ - go run main.go -``` - -Once the app starts, you should be able to navigate to http://localhost:8084/. There won't be anything there yet, but if you see `connection: open` in the top right corner that means the WebSocket connection to the back-end is established. - - -### Start sentiment scoring service - -Navigate to the [sentiment-scorer](./sentiment-scorer) directory and run: - -```shell -cd sentiment-scorer -dapr run \ - --app-id sentiment-scorer \ - --app-port 60005 \ - --app-protocol grpc \ - --components-path ./config \ - go run main.go -``` - -The last line from the above command should be - -```shell -✅ You're up and running! Both Dapr and your app logs will appear here. -``` - -### Start tweet processing service - -Navigate to the [tweet-processor](./tweet-processor) directory and run: - -```shell -cd tweet-processor -dapr run \ - --app-id tweet-processor \ - --app-port 60002 \ - --app-protocol grpc \ - --components-path ./config \ - go run main.go -``` - -The last line from the above command should be - -```shell -✅ You're up and running! Both Dapr and your app logs will appear here. -``` - - -### Start tweet provider - -Navigate to the [tweet-provider](./tweet-provider) directory and run: - -```shell -cd tweet-provider -dapr run \ - --app-id tweet-provider \ - --app-port 8080 \ - --app-protocol http \ - --components-path ./config \ - go run main.go -``` - -The last line from the above command should be - -```shell -✅ You're up and running! Both Dapr and your app logs will appear here. -``` - -### View sentiment scored tweets in the UI - -Navigate once more to http://localhost:8084/ and provided there were tweets matching your query you should now see tweets displayed in the UI. - -![](./img/ui.png) - - -## Run on Kubernetes - -> Note, these instructions assume cluster created using the [demo setup](../setup). - -### namespace - -Start by setting up the namespace for the `pipeline`: - -```shell -kubectl apply -f k8s/space.yaml -``` - -### tweet-processor - -Next, deploy `tweet-processor` and wait for it to be ready - -```shell -kubectl apply -f k8s/process-pubsub.yaml -kubectl apply -f k8s/tweeter-pubsub.yaml -kubectl apply -f k8s/processor.yaml -kubectl rollout status deployment/tweet-processor -n pipeline -``` - -Check the Dapr logs to make sure the components were registered correctly - -```shell -kubectl logs -l app=tweet-processor -c daprd -n pipeline --tail 200 -``` - -### sentiment-scorer - -Create a secret for Azure Cognitive Services - -```shell -kubectl create secret generic sentiment-secret \ - -n pipeline \ - --from-literal=token="your-azure-cognitive-service-token" -``` - -Deploy `sentiment-scorer` and wait for it to be ready - -```shell -kubectl apply -f k8s/scorer.yaml -kubectl rollout status deployment/sentiment-scorer -n pipeline -``` - -Check the logs to make sure Dapr was started correctly - -```shell -kubectl logs -l app=sentiment-scorer -c daprd -n pipeline --tail 200 -``` - -To test the service, you can first export the API token - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" -n nginx | base64 --decode) -``` - -And then invoke the service manually - -```shell -curl -i -d '{ "text": "dapr is the best" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.demo.dapr.team/v1.0/invoke/sentiment-scorer.pipeline/method/sentiment" -``` - -Response should look something like this - -```json -{ "sentiment":"positive", "confidence":1 } -``` - - -### tweet-viewer - -Create the TLS certs for this domain - -> `demo.dapr.team` is the domain I'm using for this demo - -```shell -kubectl create secret tls tls-secret \ - -n pipeline \ - --key ../setup/certs/demo.dapr.team/cert-pk.pem \ - --cert ../setup/certs/demo.dapr.team/cert-ca.pem -``` - -Deploy `tweet-viewer` along with its component - -```shell -kubectl apply -f k8s/viewer.yaml -kubectl apply -f k8s/ingress.yaml -kubectl rollout status deployment/tweet-viewer -n pipeline -``` - -Check that the ingress was updated - -```shell -kubectl get ingress -n pipeline -``` - -Should include `viewer.` - -```shell -NAME HOSTS ADDRESS PORTS AGE -ingress-rules tweets.demo.dapr.team x.x.x.x 80, 443 1m -``` - -Check in browser: https://tweets.demo.dapr.team - -### tweet-provider - -Create secret for `tweet-provider` to connect to Twitter API - -> Check [twitter developer portal](https://developer.twitter.com/en/portal/dashboard) if you need this info - -```shell -kubectl create secret generic twitter-secret -n pipeline \ - --from-literal=consumerKey="" \ - --from-literal=consumerSecret="" \ - --from-literal=accessToken="" \ - --from-literal=accessSecret="" -``` - -Deploy the `tweet-provider` service and its components - -```shell -kubectl apply -f k8s/state.yaml -kubectl apply -f k8s/twitter.yaml -kubectl apply -f k8s/provider.yaml -kubectl rollout status deployment/tweet-provider -n pipeline -``` - -Check Dapr to make sure components were registered correctly - -```shell -kubectl logs -l app=tweet-provider -c daprd -n pipeline --tail 200 -``` - -## View - -Navigate back to: https://tweets.demo.dapr.team - -If everything went well, you should see some tweets appear. - -> Note, this demo shows only tweets meeting your query posted since the viewer was started. If you chosen an unpopular search term you may have to be patient - -## Restart deployments - -If you change the components you need to apply rolling upgrades to the deployments - -```shell -kubectl rollout restart deployment/sentiment-scorer -n pipeline -kubectl rollout restart deployment/tweet-processor -n pipeline -kubectl rollout restart deployment/tweet-provider -n pipeline -kubectl rollout restart deployment/tweet-viewer -n pipeline -kubectl rollout status deployment/sentiment-scorer -n pipeline -kubectl rollout status deployment/tweet-processor -n pipeline -kubectl rollout status deployment/tweet-provider -n pipeline -kubectl rollout status deployment/tweet-viewer -n pipeline -``` - -## Debug - -Start by reviewing that all the pods are running: - -```shell -kubectl get pods -n pipeline -``` - -The response should look something like this. Notice the `2/2` container readiness in each pod and the status `Running` - -```shell -NAME READY STATUS RESTARTS AGE -sentiment-scorer-64b9b8fb48-czn5h 2/2 Running 0 10m -tweet-processor-75cff98984-skmx8 2/2 Running 0 10m -tweet-provider-b47b566b5-7vs9r 2/2 Running 0 10m -tweet-viewer-5cf465d4d8-crfk2 2/2 Running 0 10m -``` - -Next check the `daprd` and `service` logs on each deployment to make sure all the components loaded correctly. The `tweet-provider` for example would be: - -```shell -kubectl logs -l app=tweet-provider -c daprd -n pipeline --tail 300 -kubectl logs -l app=tweet-provider -c service -n pipeline --tail 300 -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Dapr pipeline demo + +Dapr supports a wide array of state and pubsub building blocks across multiple OSS, Cloud, and on-prem services. This demo shows how to use a few of these components to build tweet sentiment processing pipeline. + +![alt text](./img/overview.png "Pipeline Overview") + + +## Run in standalone mode + +### Setup + +To run these demos locally, you will have first create a secret file (`pipeline/secrets.json`). These will be used by Dapr components at runtime. To get the Twitter API secretes you will need to register your app [here](https://developer.twitter.com/en/apps/create). + +```json +{ + "Twitter": { + "ConsumerKey": "", + "ConsumerSecret": "", + "AccessToken": "", + "AccessSecret": "" + }, + "Azure": { + "CognitiveAPIKey": "" + } +} +``` + + +### Start tweet viewer app + +Navigate to the [tweet-viewer](./tweet-viewer) directory and run: + +```shell +cd tweet-viewer +dapr run \ + --app-id tweet-viewer \ + --app-port 8084 \ + --app-protocol http \ + --components-path ./config \ + go run main.go +``` + +Once the app starts, you should be able to navigate to http://localhost:8084/. There won't be anything there yet, but if you see `connection: open` in the top right corner that means the WebSocket connection to the back-end is established. + + +### Start sentiment scoring service + +Navigate to the [sentiment-scorer](./sentiment-scorer) directory and run: + +```shell +cd sentiment-scorer +dapr run \ + --app-id sentiment-scorer \ + --app-port 60005 \ + --app-protocol grpc \ + --components-path ./config \ + go run main.go +``` + +The last line from the above command should be + +```shell +✅ You're up and running! Both Dapr and your app logs will appear here. +``` + +### Start tweet processing service + +Navigate to the [tweet-processor](./tweet-processor) directory and run: + +```shell +cd tweet-processor +dapr run \ + --app-id tweet-processor \ + --app-port 60002 \ + --app-protocol grpc \ + --components-path ./config \ + go run main.go +``` + +The last line from the above command should be + +```shell +✅ You're up and running! Both Dapr and your app logs will appear here. +``` + + +### Start tweet provider + +Navigate to the [tweet-provider](./tweet-provider) directory and run: + +```shell +cd tweet-provider +dapr run \ + --app-id tweet-provider \ + --app-port 8080 \ + --app-protocol http \ + --components-path ./config \ + go run main.go +``` + +The last line from the above command should be + +```shell +✅ You're up and running! Both Dapr and your app logs will appear here. +``` + +### View sentiment scored tweets in the UI + +Navigate once more to http://localhost:8084/ and provided there were tweets matching your query you should now see tweets displayed in the UI. + +![](./img/ui.png) + + +## Run on Kubernetes + +> Note, these instructions assume cluster created using the [demo setup](../setup). + +### namespace + +Start by setting up the namespace for the `pipeline`: + +```shell +kubectl apply -f k8s/space.yaml +``` + +### tweet-processor + +Next, deploy `tweet-processor` and wait for it to be ready + +```shell +kubectl apply -f k8s/process-pubsub.yaml +kubectl apply -f k8s/tweeter-pubsub.yaml +kubectl apply -f k8s/processor.yaml +kubectl rollout status deployment/tweet-processor -n pipeline +``` + +Check the Dapr logs to make sure the components were registered correctly + +```shell +kubectl logs -l app=tweet-processor -c daprd -n pipeline --tail 200 +``` + +### sentiment-scorer + +Create a secret for Azure Cognitive Services + +```shell +kubectl create secret generic sentiment-secret \ + -n pipeline \ + --from-literal=token="your-azure-cognitive-service-token" +``` + +Deploy `sentiment-scorer` and wait for it to be ready + +```shell +kubectl apply -f k8s/scorer.yaml +kubectl rollout status deployment/sentiment-scorer -n pipeline +``` + +Check the logs to make sure Dapr was started correctly + +```shell +kubectl logs -l app=sentiment-scorer -c daprd -n pipeline --tail 200 +``` + +To test the service, you can first export the API token + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" -n nginx | base64 --decode) +``` + +And then invoke the service manually + +```shell +curl -i -d '{ "text": "dapr is the best" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.demo.dapr.team/v1.0/invoke/sentiment-scorer.pipeline/method/sentiment" +``` + +Response should look something like this + +```json +{ "sentiment":"positive", "confidence":1 } +``` + + +### tweet-viewer + +Create the TLS certs for this domain + +> `demo.dapr.team` is the domain I'm using for this demo + +```shell +kubectl create secret tls tls-secret \ + -n pipeline \ + --key ../setup/certs/demo.dapr.team/cert-pk.pem \ + --cert ../setup/certs/demo.dapr.team/cert-ca.pem +``` + +Deploy `tweet-viewer` along with its component + +```shell +kubectl apply -f k8s/viewer.yaml +kubectl apply -f k8s/ingress.yaml +kubectl rollout status deployment/tweet-viewer -n pipeline +``` + +Check that the ingress was updated + +```shell +kubectl get ingress -n pipeline +``` + +Should include `viewer.` + +```shell +NAME HOSTS ADDRESS PORTS AGE +ingress-rules tweets.demo.dapr.team x.x.x.x 80, 443 1m +``` + +Check in browser: https://tweets.demo.dapr.team + +### tweet-provider + +Create secret for `tweet-provider` to connect to Twitter API + +> Check [twitter developer portal](https://developer.twitter.com/en/portal/dashboard) if you need this info + +```shell +kubectl create secret generic twitter-secret -n pipeline \ + --from-literal=consumerKey="" \ + --from-literal=consumerSecret="" \ + --from-literal=accessToken="" \ + --from-literal=accessSecret="" +``` + +Deploy the `tweet-provider` service and its components + +```shell +kubectl apply -f k8s/state.yaml +kubectl apply -f k8s/twitter.yaml +kubectl apply -f k8s/provider.yaml +kubectl rollout status deployment/tweet-provider -n pipeline +``` + +Check Dapr to make sure components were registered correctly + +```shell +kubectl logs -l app=tweet-provider -c daprd -n pipeline --tail 200 +``` + +## View + +Navigate back to: https://tweets.demo.dapr.team + +If everything went well, you should see some tweets appear. + +> Note, this demo shows only tweets meeting your query posted since the viewer was started. If you chosen an unpopular search term you may have to be patient + +## Restart deployments + +If you change the components you need to apply rolling upgrades to the deployments + +```shell +kubectl rollout restart deployment/sentiment-scorer -n pipeline +kubectl rollout restart deployment/tweet-processor -n pipeline +kubectl rollout restart deployment/tweet-provider -n pipeline +kubectl rollout restart deployment/tweet-viewer -n pipeline +kubectl rollout status deployment/sentiment-scorer -n pipeline +kubectl rollout status deployment/tweet-processor -n pipeline +kubectl rollout status deployment/tweet-provider -n pipeline +kubectl rollout status deployment/tweet-viewer -n pipeline +``` + +## Debug + +Start by reviewing that all the pods are running: + +```shell +kubectl get pods -n pipeline +``` + +The response should look something like this. Notice the `2/2` container readiness in each pod and the status `Running` + +```shell +NAME READY STATUS RESTARTS AGE +sentiment-scorer-64b9b8fb48-czn5h 2/2 Running 0 10m +tweet-processor-75cff98984-skmx8 2/2 Running 0 10m +tweet-provider-b47b566b5-7vs9r 2/2 Running 0 10m +tweet-viewer-5cf465d4d8-crfk2 2/2 Running 0 10m +``` + +Next check the `daprd` and `service` logs on each deployment to make sure all the components loaded correctly. The `tweet-provider` for example would be: + +```shell +kubectl logs -l app=tweet-provider -c daprd -n pipeline --tail 300 +kubectl logs -l app=tweet-provider -c service -n pipeline --tail 300 +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/pipeline/k8s/ingress.yaml b/pipeline/k8s/ingress.yaml index 89c18b5..2b59791 100644 --- a/pipeline/k8s/ingress.yaml +++ b/pipeline/k8s/ingress.yaml @@ -1,21 +1,21 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: tweets-ingress-rules - namespace: pipeline - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/rewrite-target: / -spec: - tls: - - hosts: - - tweets.demo.dapr.team - secretName: tls-secret - rules: - - host: tweets.demo.dapr.team - http: - paths: - - path: / - backend: - serviceName: tweet-viewer +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: tweets-ingress-rules + namespace: pipeline + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + tls: + - hosts: + - tweets.demo.dapr.team + secretName: tls-secret + rules: + - host: tweets.demo.dapr.team + http: + paths: + - path: / + backend: + serviceName: tweet-viewer servicePort: 80 \ No newline at end of file diff --git a/pipeline/k8s/process-pubsub.yaml b/pipeline/k8s/process-pubsub.yaml index 2a95876..0481b17 100644 --- a/pipeline/k8s/process-pubsub.yaml +++ b/pipeline/k8s/process-pubsub.yaml @@ -1,19 +1,19 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: processed-tweets-pubsub - namespace: pipeline -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password - - name: allowedTopics - value: "processed-tweets" -scopes: -- tweet-processor +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: processed-tweets-pubsub + namespace: pipeline +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password + - name: allowedTopics + value: "processed-tweets" +scopes: +- tweet-processor - tweet-viewer \ No newline at end of file diff --git a/pipeline/k8s/processor.yaml b/pipeline/k8s/processor.yaml index d421040..1c4b63a 100644 --- a/pipeline/k8s/processor.yaml +++ b/pipeline/k8s/processor.yaml @@ -1,42 +1,42 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: tweet-processor - namespace: pipeline - labels: - app: tweet-processor - demo: pipeline -spec: - selector: - matchLabels: - app: tweet-processor - template: - metadata: - labels: - app: tweet-processor - demo: pipeline - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "tweet-processor" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "space-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/tweet-processor:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: SOURCE_PUBSUB_NAME - value: "tweeter-pubsub" - - name: SOURCE_TOPIC_NAME - value: "tweets" - - name: RESULT_PUBSUB_NAME - value: "processed-tweets-pubsub" - - name: RESULT_TOPIC_NAME - value: "processed-tweets" - - name: SENTIMENT_SERVICE_NAME +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tweet-processor + namespace: pipeline + labels: + app: tweet-processor + demo: pipeline +spec: + selector: + matchLabels: + app: tweet-processor + template: + metadata: + labels: + app: tweet-processor + demo: pipeline + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "tweet-processor" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "space-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/tweet-processor:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: SOURCE_PUBSUB_NAME + value: "tweeter-pubsub" + - name: SOURCE_TOPIC_NAME + value: "tweets" + - name: RESULT_PUBSUB_NAME + value: "processed-tweets-pubsub" + - name: RESULT_TOPIC_NAME + value: "processed-tweets" + - name: SENTIMENT_SERVICE_NAME value: "sentiment-scorer" \ No newline at end of file diff --git a/pipeline/k8s/provider.yaml b/pipeline/k8s/provider.yaml index 990ea99..eb70175 100644 --- a/pipeline/k8s/provider.yaml +++ b/pipeline/k8s/provider.yaml @@ -1,37 +1,37 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: tweet-provider - namespace: pipeline - labels: - app: tweet-provider - demo: pipeline -spec: - selector: - matchLabels: - app: tweet-provider - template: - metadata: - labels: - app: tweet-provider - demo: pipeline - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "tweet-provider" - dapr.io/app-port: "8080" - dapr.io/config: "space-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/tweet-provider:v0.11.1 - ports: - - containerPort: 8080 - env: - - name: ADDRESS - value: ":8080" - - name: PUBSUB_NAME - value: "tweeter-pubsub" - - name: TOPIC_NAME +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tweet-provider + namespace: pipeline + labels: + app: tweet-provider + demo: pipeline +spec: + selector: + matchLabels: + app: tweet-provider + template: + metadata: + labels: + app: tweet-provider + demo: pipeline + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "tweet-provider" + dapr.io/app-port: "8080" + dapr.io/config: "space-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/tweet-provider:v0.11.1 + ports: + - containerPort: 8080 + env: + - name: ADDRESS + value: ":8080" + - name: PUBSUB_NAME + value: "tweeter-pubsub" + - name: TOPIC_NAME value: "tweets" \ No newline at end of file diff --git a/pipeline/k8s/scorer.yaml b/pipeline/k8s/scorer.yaml index 19e415b..901aafe 100644 --- a/pipeline/k8s/scorer.yaml +++ b/pipeline/k8s/scorer.yaml @@ -1,41 +1,41 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: sentiment-scorer - namespace: pipeline - labels: - app: sentiment-scorer - demo: pipeline -spec: - selector: - matchLabels: - app: sentiment-scorer - template: - metadata: - labels: - app: sentiment-scorer - demo: pipeline - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "sentiment-scorer" - dapr.io/app-protocol: "grpc" - dapr.io/app-port: "60002" - dapr.io/config: "space-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/sentiment-scorer:v0.11.1 - ports: - - containerPort: 60002 - env: - - name: ADDRESS - value: ":60002" - - name: API_DOMAIN - value: "tweet-sentiment" - - name: API_TOKEN - valueFrom: - secretKeyRef: - name: sentiment-secret - key: token +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sentiment-scorer + namespace: pipeline + labels: + app: sentiment-scorer + demo: pipeline +spec: + selector: + matchLabels: + app: sentiment-scorer + template: + metadata: + labels: + app: sentiment-scorer + demo: pipeline + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "sentiment-scorer" + dapr.io/app-protocol: "grpc" + dapr.io/app-port: "60002" + dapr.io/config: "space-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/sentiment-scorer:v0.11.1 + ports: + - containerPort: 60002 + env: + - name: ADDRESS + value: ":60002" + - name: API_DOMAIN + value: "tweet-sentiment" + - name: API_TOKEN + valueFrom: + secretKeyRef: + name: sentiment-secret + key: token diff --git a/pipeline/k8s/space.yaml b/pipeline/k8s/space.yaml index addf3a3..cc45846 100644 --- a/pipeline/k8s/space.yaml +++ b/pipeline/k8s/space.yaml @@ -1,49 +1,49 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: pipeline ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: secret-reader - namespace: pipeline -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: dapr-secret-reader - namespace: pipeline -subjects: -- kind: ServiceAccount - name: default -roleRef: - kind: Role - name: secret-reader - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: zipkin - namespace: pipeline -spec: - type: exporters.zipkin - metadata: - - name: enabled - value: "true" - - name: exporterAddress - value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: space-config - namespace: pipeline -spec: - tracing: +apiVersion: v1 +kind: Namespace +metadata: + name: pipeline +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secret-reader + namespace: pipeline +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dapr-secret-reader + namespace: pipeline +subjects: +- kind: ServiceAccount + name: default +roleRef: + kind: Role + name: secret-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: zipkin + namespace: pipeline +spec: + type: exporters.zipkin + metadata: + - name: enabled + value: "true" + - name: exporterAddress + value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: space-config + namespace: pipeline +spec: + tracing: samplingRate: "1" \ No newline at end of file diff --git a/pipeline/k8s/state.yaml b/pipeline/k8s/state.yaml index 6589409..841cec5 100644 --- a/pipeline/k8s/state.yaml +++ b/pipeline/k8s/state.yaml @@ -1,22 +1,22 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: tweet-store - namespace: pipeline -spec: - type: state.mongodb - metadata: - - name: host - value: mongo-mongodb-headless.mongo.svc.cluster.local:27017 - - name: username - value: dapr - - name: password - secretKeyRef: - name: mongo-secret - key: password - - name: databaseName - value: dapr - - name: collectionName - value: audit -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: tweet-store + namespace: pipeline +spec: + type: state.mongodb + metadata: + - name: host + value: mongo-mongodb-headless.mongo.svc.cluster.local:27017 + - name: username + value: dapr + - name: password + secretKeyRef: + name: mongo-secret + key: password + - name: databaseName + value: dapr + - name: collectionName + value: audit +scopes: - tweet-provider \ No newline at end of file diff --git a/pipeline/k8s/tweeter-pubsub.yaml b/pipeline/k8s/tweeter-pubsub.yaml index a18026a..32ad56c 100644 --- a/pipeline/k8s/tweeter-pubsub.yaml +++ b/pipeline/k8s/tweeter-pubsub.yaml @@ -1,19 +1,19 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: tweeter-pubsub - namespace: pipeline -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: redis-master.redis.svc.cluster.local:6379 - - name: redisPassword - secretKeyRef: - name: redis-secret - key: password - - name: allowedTopics - value: "tweets" -scopes: -- tweet-provider +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: tweeter-pubsub + namespace: pipeline +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: redis-master.redis.svc.cluster.local:6379 + - name: redisPassword + secretKeyRef: + name: redis-secret + key: password + - name: allowedTopics + value: "tweets" +scopes: +- tweet-provider - tweet-processor \ No newline at end of file diff --git a/pipeline/k8s/twitter.yaml b/pipeline/k8s/twitter.yaml index a7c5859..cf41cb7 100644 --- a/pipeline/k8s/twitter.yaml +++ b/pipeline/k8s/twitter.yaml @@ -1,28 +1,28 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: tweets - namespace: pipeline -spec: - type: bindings.twitter - metadata: - - name: consumerKey - secretKeyRef: - name: twitter-secret - key: consumerKey - - name: consumerSecret - secretKeyRef: - name: twitter-secret - key: consumerSecret - - name: accessToken - secretKeyRef: - name: twitter-secret - key: accessToken - - name: accessSecret - secretKeyRef: - name: twitter-secret - key: accessSecret - - name: query - value: "dapr" # use more common term for demo -scopes: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: tweets + namespace: pipeline +spec: + type: bindings.twitter + metadata: + - name: consumerKey + secretKeyRef: + name: twitter-secret + key: consumerKey + - name: consumerSecret + secretKeyRef: + name: twitter-secret + key: consumerSecret + - name: accessToken + secretKeyRef: + name: twitter-secret + key: accessToken + - name: accessSecret + secretKeyRef: + name: twitter-secret + key: accessSecret + - name: query + value: "dapr" # use more common term for demo +scopes: - tweet-provider \ No newline at end of file diff --git a/pipeline/k8s/viewer.yaml b/pipeline/k8s/viewer.yaml index f3b8a6f..b90a038 100644 --- a/pipeline/k8s/viewer.yaml +++ b/pipeline/k8s/viewer.yaml @@ -1,51 +1,51 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: tweet-viewer - namespace: pipeline - labels: - app: tweet-viewer - demo: pipeline -spec: - selector: - matchLabels: - app: tweet-viewer - template: - metadata: - labels: - app: tweet-viewer - demo: pipeline - annotations: - dapr.io/enabled: "true" - dapr.io/app-id: "tweet-viewer" - dapr.io/app-protocol: "http" - dapr.io/app-port: "8084" - dapr.io/config: "space-config" - dapr.io/log-as-json: "true" - dapr.io/log-level: "debug" - spec: - containers: - - name: service - image: mchmarny/tweet-viewer:v0.11.1 - ports: - - containerPort: 8084 - env: - - name: ADDRESS - value: ":8084" - - name: PUBSUB_NAME - value: "processed-tweets-pubsub" - - name: TOPIC_NAME - value: "processed-tweets" ---- -apiVersion: v1 -kind: Service -metadata: - name: tweet-viewer - namespace: pipeline -spec: - selector: - app: tweet-viewer - ports: - - protocol: TCP - port: 80 +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tweet-viewer + namespace: pipeline + labels: + app: tweet-viewer + demo: pipeline +spec: + selector: + matchLabels: + app: tweet-viewer + template: + metadata: + labels: + app: tweet-viewer + demo: pipeline + annotations: + dapr.io/enabled: "true" + dapr.io/app-id: "tweet-viewer" + dapr.io/app-protocol: "http" + dapr.io/app-port: "8084" + dapr.io/config: "space-config" + dapr.io/log-as-json: "true" + dapr.io/log-level: "debug" + spec: + containers: + - name: service + image: mchmarny/tweet-viewer:v0.11.1 + ports: + - containerPort: 8084 + env: + - name: ADDRESS + value: ":8084" + - name: PUBSUB_NAME + value: "processed-tweets-pubsub" + - name: TOPIC_NAME + value: "processed-tweets" +--- +apiVersion: v1 +kind: Service +metadata: + name: tweet-viewer + namespace: pipeline +spec: + selector: + app: tweet-viewer + ports: + - protocol: TCP + port: 80 targetPort: 8084 \ No newline at end of file diff --git a/pipeline/sentiment-scorer/Dockerfile b/pipeline/sentiment-scorer/Dockerfile index f5904ac..a5c01c7 100644 --- a/pipeline/sentiment-scorer/Dockerfile +++ b/pipeline/sentiment-scorer/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/pipeline/sentiment-scorer/Makefile b/pipeline/sentiment-scorer/Makefile index c8ee289..828d27e 100644 --- a/pipeline/sentiment-scorer/Makefile +++ b/pipeline/sentiment-scorer/Makefile @@ -1,64 +1,64 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=sentiment-scorer -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: tidy debug invoke image deploy call lint clean tag -all: help - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -run: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60005 \ - --app-protocol grpc \ - --dapr-http-port 3505 \ - --components-path ./config \ - --log-level debug \ - go run main.go - -invoke: ## Invokes service through Dapr API - curl -i -d '{ "text": "dapr is the best" }' \ - -H "Content-type: application/json" \ - "http://localhost:3505/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" - curl -i -d '{ "text": "spinach is the worst" }' \ - -H "Content-type: application/json" \ - "http://localhost:3505/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" - curl -i -d '{ "text": "happy alram make me sad" }' \ - -H "Content-type: application/json" \ - "http://localhost:3505/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" - -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f deployment.yaml - kubectl rollout restart deployment/sentiment-scorer - kubectl rollout restart deployment/nginx-ingress-nginx-controller - kubectl rollout status deployment/nginx-ingress-nginx-controller - -api-invoke: ## Invokes service through Dapr API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -d '{ "text": "dapr is the best" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=sentiment-scorer +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: tidy debug invoke image deploy call lint clean tag +all: help + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +run: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60005 \ + --app-protocol grpc \ + --dapr-http-port 3505 \ + --components-path ./config \ + --log-level debug \ + go run main.go + +invoke: ## Invokes service through Dapr API + curl -i -d '{ "text": "dapr is the best" }' \ + -H "Content-type: application/json" \ + "http://localhost:3505/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" + curl -i -d '{ "text": "spinach is the worst" }' \ + -H "Content-type: application/json" \ + "http://localhost:3505/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" + curl -i -d '{ "text": "happy alram make me sad" }' \ + -H "Content-type: application/json" \ + "http://localhost:3505/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" + +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f deployment.yaml + kubectl rollout restart deployment/sentiment-scorer + kubectl rollout restart deployment/nginx-ingress-nginx-controller + kubectl rollout status deployment/nginx-ingress-nginx-controller + +api-invoke: ## Invokes service through Dapr API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -d '{ "text": "dapr is the best" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/invoke/$(SERVICE_NAME)/method/sentiment" + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/pipeline/sentiment-scorer/README.md b/pipeline/sentiment-scorer/README.md index ab5365a..e044d1c 100644 --- a/pipeline/sentiment-scorer/README.md +++ b/pipeline/sentiment-scorer/README.md @@ -1,69 +1,69 @@ -# grpc-service - -For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) - -## Run - -To run this demo in Dapr, run: - -```shell -API_TOKEN="your-azure-cognitive-service-token" dapr run \ - --app-id sentiment-scorer \ - --app-port 60001 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - --components-path ./config \ - go run main.go -``` - -## Deploy - -Create a `sentiment-secret` - -```shell -kubectl create secret generic sentiment-secret --from-literal=token="your-azure-cognitive-service-token" -``` - -Deploy and wait for the pod to be ready - -```shell -kubectl apply -f deployment.yaml -kubectl rollout restart deployment/sentiment-scorer -kubectl rollout restart deployment/nginx-ingress-nginx-controller -kubectl rollout status deployment/nginx-ingress-nginx-controller -``` - -Follow logs - -```shell -kubectl logs -l demo=sentiment -c service -f -``` - -In a separate terminal session export API token - -```shell -export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) -``` - -And invoke the service - -```shell -curl -d '{ "text": "dapr is the best" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: ${API_TOKEN}" \ - "https://api.cloudylabs.dev/v1.0/invoke/sentiment-scorer/method/sentiment" -``` - -Response should look something like this - -```json -{ "sentiment":"positive", "confidence":1 } -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](./LICENSE) +# grpc-service + +For more information about service invocation see the [Dapr docs](https://github.com/dapr/docs/tree/master/concepts/service-invocation) + +## Run + +To run this demo in Dapr, run: + +```shell +API_TOKEN="your-azure-cognitive-service-token" dapr run \ + --app-id sentiment-scorer \ + --app-port 60001 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + --components-path ./config \ + go run main.go +``` + +## Deploy + +Create a `sentiment-secret` + +```shell +kubectl create secret generic sentiment-secret --from-literal=token="your-azure-cognitive-service-token" +``` + +Deploy and wait for the pod to be ready + +```shell +kubectl apply -f deployment.yaml +kubectl rollout restart deployment/sentiment-scorer +kubectl rollout restart deployment/nginx-ingress-nginx-controller +kubectl rollout status deployment/nginx-ingress-nginx-controller +``` + +Follow logs + +```shell +kubectl logs -l demo=sentiment -c service -f +``` + +In a separate terminal session export API token + +```shell +export API_TOKEN=$(kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode) +``` + +And invoke the service + +```shell +curl -d '{ "text": "dapr is the best" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: ${API_TOKEN}" \ + "https://api.cloudylabs.dev/v1.0/invoke/sentiment-scorer/method/sentiment" +``` + +Response should look something like this + +```json +{ "sentiment":"positive", "confidence":1 } +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](./LICENSE) diff --git a/pipeline/sentiment-scorer/config/secret.yaml b/pipeline/sentiment-scorer/config/secret.yaml index 837a1b1..4ee7cff 100644 --- a/pipeline/sentiment-scorer/config/secret.yaml +++ b/pipeline/sentiment-scorer/config/secret.yaml @@ -1,9 +1,9 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: pipeline-secrets -spec: - type: secretstores.local.file - metadata: - - name: secretsFile +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pipeline-secrets +spec: + type: secretstores.local.file + metadata: + - name: secretsFile value: "/etc/dapr/secrets.json" \ No newline at end of file diff --git a/pipeline/sentiment-scorer/go.mod b/pipeline/sentiment-scorer/go.mod index 8166cd2..9d9746e 100644 --- a/pipeline/sentiment-scorer/go.mod +++ b/pipeline/sentiment-scorer/go.mod @@ -1,11 +1,11 @@ -module github.com/mchmarny/dapr-demos/pipeline/sentiment-scorer - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect - google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect -) +module github.com/mchmarny/dapr-demos/pipeline/sentiment-scorer + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect + google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect +) diff --git a/pipeline/sentiment-scorer/go.sum b/pipeline/sentiment-scorer/go.sum index adb2906..96fb780 100644 --- a/pipeline/sentiment-scorer/go.sum +++ b/pipeline/sentiment-scorer/go.sum @@ -1,106 +1,106 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pipeline/sentiment-scorer/main.go b/pipeline/sentiment-scorer/main.go index 65d2495..f3e3f74 100644 --- a/pipeline/sentiment-scorer/main.go +++ b/pipeline/sentiment-scorer/main.go @@ -1,188 +1,188 @@ -package main - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "log" - "net/http" - "os" - "strings" - "time" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/pkg/errors" -) - -const ( - languageDefault = "en" - secretStoreName = "pipeline-secrets" - secretStoreKey = "Azure:CognitiveAPIKey" -) - -var ( - logger = log.New(os.Stdout, "", 0) - - serviceAddress = getEnvVar("ADDRESS", ":60005") - apiToken = getEnvVar("API_TOKEN", "") - apiDomain = getEnvVar("API_DOMAIN", "tweet-sentiment") - - apiURL = fmt.Sprintf("https://%s.cognitiveservices.azure.com/text/analytics/v3.0/sentiment", apiDomain) -) - -func main() { - - // create serving server - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - // add handler to the service - s.AddServiceInvocationHandler("sentiment", sentimentHandler) - - // start the server to handle incoming events - log.Printf("starting server at %s...", serviceAddress) - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func sentimentHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - logger.Printf("Processing: %s", in.Data) - var req map[string]string - if err := json.Unmarshal(in.Data, &req); err != nil { - return nil, errors.Wrapf(err, "error deserializing data: %s", in.Data) - } - - score, err := getSentiment(ctx, req["language"], req["text"]) - if err != nil { - logger.Printf("error scoring sentiment: %v", err) - return nil, errors.Wrapf(err, "error scoring sentiment: %s", in.Data) - } - - b, err := json.Marshal(score) - if err != nil { - return nil, errors.Wrapf(err, "error serializing score: %v", score) - } - - logger.Printf("Processed: %s", b) - return &common.Content{ - ContentType: "application/json", - Data: b, - }, nil -} - -// SentimentScore represents sentiment result -type SentimentScore struct { - Sentiment string `json:"sentiment"` - Confidence float64 `json:"confidence"` -} - -func getSentiment(ctx context.Context, lang, text string) (out *SentimentScore, err error) { - if text == "" { - return nil, errors.New("text required") - } - - if lang == "" { - lang = languageDefault - } - - if apiToken == "" { - apiToken = getSecret(secretStoreName, secretStoreKey) - } - - r := fmt.Sprintf(`{ - "documents": [{ - "language": "%s", - "id": "1", - "text": "%s" - }] - }`, lang, text) - - req, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewBuffer([]byte(r))) - if err != nil { - return nil, errors.Wrapf(err, "error creating request from: %v", r) - } - - req = req.WithContext(ctx) - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Ocp-Apim-Subscription-Key", apiToken) - - client := http.Client{Timeout: time.Second * 5} - res, err := client.Do(req) - if err != nil { - return nil, errors.Wrapf(err, "error posting to: %s", apiURL) - } - - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("invalid API response status: %d", res.StatusCode) - } - defer res.Body.Close() - - // dump, _ := httputil.DumpResponse(res, true) - // logger.Printf("response: %s", dump) - - var rez struct { - Documents []struct { - Sentiment string `json:"sentiment"` - Scores struct { - Positive float64 `json:"positive"` - Neutral float64 `json:"neutral"` - Negative float64 `json:"negative"` - Mixed float64 `json:"mixed"` - } `json:"confidenceScores"` - } `json:"documents"` - } - - if err := json.NewDecoder(res.Body).Decode(&rez); err != nil { - return nil, errors.Wrap(err, "error decoding API response") - } - - if len(rez.Documents) != 1 { - return nil, errors.Wrapf(err, "invalid response, expected 1 document, got %d", len(rez.Documents)) - } - - doc := rez.Documents[0] - out = &SentimentScore{ - Sentiment: doc.Sentiment, - } - - switch out.Sentiment { - case "positive": - out.Confidence = rez.Documents[0].Scores.Positive - case "negative": - out.Confidence = rez.Documents[0].Scores.Negative - case "neutral": - out.Confidence = rez.Documents[0].Scores.Neutral - case "mixed": - out.Confidence = rez.Documents[0].Scores.Mixed - default: - return nil, fmt.Errorf("invalid sentiment: %s", out.Sentiment) - } - - return -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} - -func getSecret(store, key string) string { - // try to find it in Dapr secret store - c, err := dapr.NewClient() - if err != nil { - logger.Fatal("unable to create Dapr client") - } - if m, err := c.GetSecret(context.Background(), store, key, map[string]string{}); err == nil { - return m[key] - } - logger.Fatalf("no item found in Dapr secret store for %s", key) - return "" -} +package main + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "strings" + "time" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/pkg/errors" +) + +const ( + languageDefault = "en" + secretStoreName = "pipeline-secrets" + secretStoreKey = "Azure:CognitiveAPIKey" +) + +var ( + logger = log.New(os.Stdout, "", 0) + + serviceAddress = getEnvVar("ADDRESS", ":60005") + apiToken = getEnvVar("API_TOKEN", "") + apiDomain = getEnvVar("API_DOMAIN", "tweet-sentiment") + + apiURL = fmt.Sprintf("https://%s.cognitiveservices.azure.com/text/analytics/v3.0/sentiment", apiDomain) +) + +func main() { + + // create serving server + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + // add handler to the service + s.AddServiceInvocationHandler("sentiment", sentimentHandler) + + // start the server to handle incoming events + log.Printf("starting server at %s...", serviceAddress) + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func sentimentHandler(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + logger.Printf("Processing: %s", in.Data) + var req map[string]string + if err := json.Unmarshal(in.Data, &req); err != nil { + return nil, errors.Wrapf(err, "error deserializing data: %s", in.Data) + } + + score, err := getSentiment(ctx, req["language"], req["text"]) + if err != nil { + logger.Printf("error scoring sentiment: %v", err) + return nil, errors.Wrapf(err, "error scoring sentiment: %s", in.Data) + } + + b, err := json.Marshal(score) + if err != nil { + return nil, errors.Wrapf(err, "error serializing score: %v", score) + } + + logger.Printf("Processed: %s", b) + return &common.Content{ + ContentType: "application/json", + Data: b, + }, nil +} + +// SentimentScore represents sentiment result +type SentimentScore struct { + Sentiment string `json:"sentiment"` + Confidence float64 `json:"confidence"` +} + +func getSentiment(ctx context.Context, lang, text string) (out *SentimentScore, err error) { + if text == "" { + return nil, errors.New("text required") + } + + if lang == "" { + lang = languageDefault + } + + if apiToken == "" { + apiToken = getSecret(secretStoreName, secretStoreKey) + } + + r := fmt.Sprintf(`{ + "documents": [{ + "language": "%s", + "id": "1", + "text": "%s" + }] + }`, lang, text) + + req, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewBuffer([]byte(r))) + if err != nil { + return nil, errors.Wrapf(err, "error creating request from: %v", r) + } + + req = req.WithContext(ctx) + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Ocp-Apim-Subscription-Key", apiToken) + + client := http.Client{Timeout: time.Second * 5} + res, err := client.Do(req) + if err != nil { + return nil, errors.Wrapf(err, "error posting to: %s", apiURL) + } + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("invalid API response status: %d", res.StatusCode) + } + defer res.Body.Close() + + // dump, _ := httputil.DumpResponse(res, true) + // logger.Printf("response: %s", dump) + + var rez struct { + Documents []struct { + Sentiment string `json:"sentiment"` + Scores struct { + Positive float64 `json:"positive"` + Neutral float64 `json:"neutral"` + Negative float64 `json:"negative"` + Mixed float64 `json:"mixed"` + } `json:"confidenceScores"` + } `json:"documents"` + } + + if err := json.NewDecoder(res.Body).Decode(&rez); err != nil { + return nil, errors.Wrap(err, "error decoding API response") + } + + if len(rez.Documents) != 1 { + return nil, errors.Wrapf(err, "invalid response, expected 1 document, got %d", len(rez.Documents)) + } + + doc := rez.Documents[0] + out = &SentimentScore{ + Sentiment: doc.Sentiment, + } + + switch out.Sentiment { + case "positive": + out.Confidence = rez.Documents[0].Scores.Positive + case "negative": + out.Confidence = rez.Documents[0].Scores.Negative + case "neutral": + out.Confidence = rez.Documents[0].Scores.Neutral + case "mixed": + out.Confidence = rez.Documents[0].Scores.Mixed + default: + return nil, fmt.Errorf("invalid sentiment: %s", out.Sentiment) + } + + return +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} + +func getSecret(store, key string) string { + // try to find it in Dapr secret store + c, err := dapr.NewClient() + if err != nil { + logger.Fatal("unable to create Dapr client") + } + if m, err := c.GetSecret(context.Background(), store, key, map[string]string{}); err == nil { + return m[key] + } + logger.Fatalf("no item found in Dapr secret store for %s", key) + return "" +} diff --git a/pipeline/tweet-processor/Dockerfile b/pipeline/tweet-processor/Dockerfile index f5904ac..a5c01c7 100644 --- a/pipeline/tweet-processor/Dockerfile +++ b/pipeline/tweet-processor/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/pipeline/tweet-processor/Makefile b/pipeline/tweet-processor/Makefile index 23e24f6..0ff83cd 100644 --- a/pipeline/tweet-processor/Makefile +++ b/pipeline/tweet-processor/Makefile @@ -1,54 +1,54 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=tweet-processor -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag -all: help - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -debug: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 60002 \ - --app-protocol grpc \ - --dapr-http-port 3500 \ - --components-path ./config \ - --log-level debug \ - go run main.go - -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/source-pubsub.yaml - kubectl apply -f k8s/result-pubsub.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/tweet-processor - kubectl rollout status deployment/tweet-processor - -event: ## Publishes sample JSON message to Dapr pubsub API - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) - curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.cloudylabs.dev/v1.0/publish/grpc-events/messages" - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=tweet-processor +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag +all: help + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +debug: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 60002 \ + --app-protocol grpc \ + --dapr-http-port 3500 \ + --components-path ./config \ + --log-level debug \ + go run main.go + +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/source-pubsub.yaml + kubectl apply -f k8s/result-pubsub.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/tweet-processor + kubectl rollout status deployment/tweet-processor + +event: ## Publishes sample JSON message to Dapr pubsub API + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -o jsonpath="{.data.token}" | base64 --decode)) + curl -d '{ "from": "John", "to": "Lary", "message": "hi" }' \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.cloudylabs.dev/v1.0/publish/grpc-events/messages" + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/pipeline/tweet-processor/config/result-pubsub.yaml b/pipeline/tweet-processor/config/result-pubsub.yaml index 1ac1bc2..7c549d7 100644 --- a/pipeline/tweet-processor/config/result-pubsub.yaml +++ b/pipeline/tweet-processor/config/result-pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: processed-tweets-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: processed-tweets-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/pipeline/tweet-processor/config/source-pubsub.yaml b/pipeline/tweet-processor/config/source-pubsub.yaml index 3480cd3..b2903e1 100644 --- a/pipeline/tweet-processor/config/source-pubsub.yaml +++ b/pipeline/tweet-processor/config/source-pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: tweeter-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: tweeter-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/pipeline/tweet-processor/go.mod b/pipeline/tweet-processor/go.mod index 001970d..856ded4 100644 --- a/pipeline/tweet-processor/go.mod +++ b/pipeline/tweet-processor/go.mod @@ -1,11 +1,11 @@ -module github.com/mchmarny/dapr-demos/pipeline/tweet-processor - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect - google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect -) +module github.com/mchmarny/dapr-demos/pipeline/tweet-processor + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect + google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect +) diff --git a/pipeline/tweet-processor/go.sum b/pipeline/tweet-processor/go.sum index adb2906..96fb780 100644 --- a/pipeline/tweet-processor/go.sum +++ b/pipeline/tweet-processor/go.sum @@ -1,106 +1,106 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pipeline/tweet-processor/main.go b/pipeline/tweet-processor/main.go index 6fa5f64..61a1a27 100644 --- a/pipeline/tweet-processor/main.go +++ b/pipeline/tweet-processor/main.go @@ -1,161 +1,161 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - "os" - "strings" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/pkg/errors" -) - -var ( - logger = log.New(os.Stdout, "", 0) - serviceAddress = getEnvVar("ADDRESS", ":60002") - - srcPubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "tweeter-pubsub") - srcTopicName = getEnvVar("SOURCE_TOPIC_NAME", "tweets") - - resultPubSubName = getEnvVar("RESULT_PUBSUB_NAME", "processed-tweets-pubsub") - resultTopicName = getEnvVar("RESULT_TOPIC_NAME", "processed-tweets") - - sentimentServiceName = getEnvVar("SENTIMENT_SERVICE_NAME", "sentiment-scorer") - - client dapr.Client -) - -func main() { - // create Dapr service - s, err := daprd.NewService(serviceAddress) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - c, err := dapr.NewClient() - if err != nil { - log.Fatalf("error creating Dapr client: %v", err) - } - client = c - defer client.Close() - - // add handler to the service - subscription := &common.Subscription{ - PubsubName: srcPubSubName, - Topic: srcTopicName, - } - s.AddTopicEventHandler(subscription, tweetHandler) - - // start the server to handle incoming events - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func topicDataToSentimentRequest(b []byte) (s *SentimentRequest, err error) { - var t TweetText - if err := json.Unmarshal(b, &t); err != nil { - return nil, errors.Wrapf(err, "error deserializing tweet into request: %s", b) - } - - s = &SentimentRequest{ - Text: t.Text, - Language: t.Lang, - } - - if t.Extended.Text != "" { - s.Text = t.Extended.Text - } - - return -} - -func getSentimentScore(ctx context.Context, req *SentimentRequest) (score *SentimentScore, err error) { - cb, err := json.Marshal(req) - if err != nil { - return nil, errors.Wrap(err, "unable to serialize sentiment request") - } - - c := &dapr.DataContent{ContentType: "application/json", Data: cb} - - b, err := client.InvokeServiceWithContent(ctx, sentimentServiceName, "sentiment", c) - if err != nil { - return nil, errors.Wrap(err, "error invoking sentiment service") - } - - var s SentimentScore - if err := json.Unmarshal(b, &s); err != nil { - return nil, errors.Wrap(err, "error deserializing sentiment") - } - - return &s, nil -} - -func tweetHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - logger.Printf("Processing pubsub:%s/topic:%s id:%s", e.PubsubName, e.Topic, e.ID) - - b, ok := e.Data.([]byte) - if !ok { - return false, fmt.Errorf("invalid data type, expected []bytes: %T", e.Data) - } - - sentReq, err := topicDataToSentimentRequest(b) - if err != nil { - return false, errors.Wrap(err, "error getting tweet text") - } - - sentScore, err := getSentimentScore(ctx, sentReq) - if err != nil { - return true, errors.Wrap(err, "error getting sentiment score") - } - - var tweetMap map[string]interface{} - if err := json.Unmarshal(b, &tweetMap); err != nil { - return true, errors.Wrap(err, "error deserializing content into map") - } - - tweetMap["sentiment"] = sentScore - content, err := json.Marshal(tweetMap) - if err != nil { - return true, errors.Wrap(err, "unable to serialize tweet map content") - } - - if err := client.PublishEvent(ctx, resultPubSubName, resultTopicName, content); err != nil { - return true, errors.Wrapf(err, "error publishing to %s/%s", resultPubSubName, resultTopicName) - } - - logger.Printf("Processed tweet:%s - %v", e.ID, sentScore) - return false, nil -} - -// TweetText represents only the text of tweet for sentiment -type TweetText struct { - Text string `json:"text"` - Lang string `json:"lang"` - Extended struct { - Text string `json:"full_text"` - } `json:"extended_tweet"` -} - -// SentimentRequest represents the sentiment request -type SentimentRequest struct { - Text string `json:"text"` - Language string `json:"language"` -} - -// SentimentScore represents sentiment result -type SentimentScore struct { - Sentiment string `json:"sentiment"` - Confidence float64 `json:"confidence"` -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + "strings" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/pkg/errors" +) + +var ( + logger = log.New(os.Stdout, "", 0) + serviceAddress = getEnvVar("ADDRESS", ":60002") + + srcPubSubName = getEnvVar("SOURCE_PUBSUB_NAME", "tweeter-pubsub") + srcTopicName = getEnvVar("SOURCE_TOPIC_NAME", "tweets") + + resultPubSubName = getEnvVar("RESULT_PUBSUB_NAME", "processed-tweets-pubsub") + resultTopicName = getEnvVar("RESULT_TOPIC_NAME", "processed-tweets") + + sentimentServiceName = getEnvVar("SENTIMENT_SERVICE_NAME", "sentiment-scorer") + + client dapr.Client +) + +func main() { + // create Dapr service + s, err := daprd.NewService(serviceAddress) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + c, err := dapr.NewClient() + if err != nil { + log.Fatalf("error creating Dapr client: %v", err) + } + client = c + defer client.Close() + + // add handler to the service + subscription := &common.Subscription{ + PubsubName: srcPubSubName, + Topic: srcTopicName, + } + s.AddTopicEventHandler(subscription, tweetHandler) + + // start the server to handle incoming events + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func topicDataToSentimentRequest(b []byte) (s *SentimentRequest, err error) { + var t TweetText + if err := json.Unmarshal(b, &t); err != nil { + return nil, errors.Wrapf(err, "error deserializing tweet into request: %s", b) + } + + s = &SentimentRequest{ + Text: t.Text, + Language: t.Lang, + } + + if t.Extended.Text != "" { + s.Text = t.Extended.Text + } + + return +} + +func getSentimentScore(ctx context.Context, req *SentimentRequest) (score *SentimentScore, err error) { + cb, err := json.Marshal(req) + if err != nil { + return nil, errors.Wrap(err, "unable to serialize sentiment request") + } + + c := &dapr.DataContent{ContentType: "application/json", Data: cb} + + b, err := client.InvokeServiceWithContent(ctx, sentimentServiceName, "sentiment", c) + if err != nil { + return nil, errors.Wrap(err, "error invoking sentiment service") + } + + var s SentimentScore + if err := json.Unmarshal(b, &s); err != nil { + return nil, errors.Wrap(err, "error deserializing sentiment") + } + + return &s, nil +} + +func tweetHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + logger.Printf("Processing pubsub:%s/topic:%s id:%s", e.PubsubName, e.Topic, e.ID) + + b, ok := e.Data.([]byte) + if !ok { + return false, fmt.Errorf("invalid data type, expected []bytes: %T", e.Data) + } + + sentReq, err := topicDataToSentimentRequest(b) + if err != nil { + return false, errors.Wrap(err, "error getting tweet text") + } + + sentScore, err := getSentimentScore(ctx, sentReq) + if err != nil { + return true, errors.Wrap(err, "error getting sentiment score") + } + + var tweetMap map[string]interface{} + if err := json.Unmarshal(b, &tweetMap); err != nil { + return true, errors.Wrap(err, "error deserializing content into map") + } + + tweetMap["sentiment"] = sentScore + content, err := json.Marshal(tweetMap) + if err != nil { + return true, errors.Wrap(err, "unable to serialize tweet map content") + } + + if err := client.PublishEvent(ctx, resultPubSubName, resultTopicName, content); err != nil { + return true, errors.Wrapf(err, "error publishing to %s/%s", resultPubSubName, resultTopicName) + } + + logger.Printf("Processed tweet:%s - %v", e.ID, sentScore) + return false, nil +} + +// TweetText represents only the text of tweet for sentiment +type TweetText struct { + Text string `json:"text"` + Lang string `json:"lang"` + Extended struct { + Text string `json:"full_text"` + } `json:"extended_tweet"` +} + +// SentimentRequest represents the sentiment request +type SentimentRequest struct { + Text string `json:"text"` + Language string `json:"language"` +} + +// SentimentScore represents sentiment result +type SentimentScore struct { + Sentiment string `json:"sentiment"` + Confidence float64 `json:"confidence"` +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/pipeline/tweet-provider/Dockerfile b/pipeline/tweet-provider/Dockerfile index f5904ac..a5c01c7 100644 --- a/pipeline/tweet-provider/Dockerfile +++ b/pipeline/tweet-provider/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/pipeline/tweet-provider/Makefile b/pipeline/tweet-provider/Makefile index 0daccc2..fd49b7f 100644 --- a/pipeline/tweet-provider/Makefile +++ b/pipeline/tweet-provider/Makefile @@ -1,45 +1,45 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=tweet-provider -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: help tidy build run image lint tag clean -all: help - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -run: ## Runs uncompiled code it in Dapr in debug mode - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 8080 \ - --app-protocol http \ - --components-path ./config \ - go run main.go - -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/pubsub.yaml - kubectl apply -f k8s/twitter.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/tweet-provider - kubectl rollout status deployment/tweet-provider - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=tweet-provider +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: help tidy build run image lint tag clean +all: help + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +run: ## Runs uncompiled code it in Dapr in debug mode + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 8080 \ + --app-protocol http \ + --components-path ./config \ + go run main.go + +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/pubsub.yaml + kubectl apply -f k8s/twitter.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/tweet-provider + kubectl rollout status deployment/tweet-provider + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/pipeline/tweet-provider/README.md b/pipeline/tweet-provider/README.md index ade34a6..d7fbef4 100644 --- a/pipeline/tweet-provider/README.md +++ b/pipeline/tweet-provider/README.md @@ -1,85 +1,85 @@ -# Tweet Provider Demo - -## Setup - -To run these demos locally, you will have first create a secret file (`pipeline/secrets.json`). These will be used by Dapr components at runtime. To get the Twitter API secretes you will need to register your app [here](https://developer.twitter.com/en/apps/create). - -```json -{ - "Twitter": { - "ConsumerKey": "", - "ConsumerSecret": "", - "AccessToken": "", - "AccessSecret": "" - } -} -``` - -## Run it - -### Standalone Mode - -Navigate to the [tweet-provider](./tweet-provider) directory and run: - -```shell -cd tweet-provider -dapr run \ - --app-id tweet-provider \ - --app-port 8080 \ - --app-protocol http \ - --components-path ./config \ - go run main.go -``` - -The last line from the above command should be - -```shell -✅ You're up and running! Both Dapr and your app logs will appear here. -``` - -Your tweets should appear in the logs now - - -### Kubernetes - - -Create secret for `tweet-provider` to connect to Twitter API - -```shell -kubectl create secret generic twitter-secret \ - --from-literal=consumerKey="" \ - --from-literal=consumerSecret="" \ - --from-literal=accessToken="" \ - --from-literal=accessSecret="" -``` - -Deploy the `tweet-provider` service and its components - -```shell -kubectl apply -f tweet-provider/k8s/state.yaml -kubectl apply -f tweet-provider/k8s/pubsub.yaml -kubectl apply -f tweet-provider/k8s/twitter.yaml -kubectl apply -f tweet-provider/k8s/deployment.yaml -kubectl rollout status deployment/tweet-provider -``` - -If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready - -```shell -kubectl rollout restart deployment/tweet-provider -kubectl rollout status deployment/tweet-provider -``` - -Check Dapr to make sure components were registered correctly - -```shell -kubectl logs -l app=tweet-provider -c daprd --tail 200 -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - -This software is released under the [MIT](../LICENSE) +# Tweet Provider Demo + +## Setup + +To run these demos locally, you will have first create a secret file (`pipeline/secrets.json`). These will be used by Dapr components at runtime. To get the Twitter API secretes you will need to register your app [here](https://developer.twitter.com/en/apps/create). + +```json +{ + "Twitter": { + "ConsumerKey": "", + "ConsumerSecret": "", + "AccessToken": "", + "AccessSecret": "" + } +} +``` + +## Run it + +### Standalone Mode + +Navigate to the [tweet-provider](./tweet-provider) directory and run: + +```shell +cd tweet-provider +dapr run \ + --app-id tweet-provider \ + --app-port 8080 \ + --app-protocol http \ + --components-path ./config \ + go run main.go +``` + +The last line from the above command should be + +```shell +✅ You're up and running! Both Dapr and your app logs will appear here. +``` + +Your tweets should appear in the logs now + + +### Kubernetes + + +Create secret for `tweet-provider` to connect to Twitter API + +```shell +kubectl create secret generic twitter-secret \ + --from-literal=consumerKey="" \ + --from-literal=consumerSecret="" \ + --from-literal=accessToken="" \ + --from-literal=accessSecret="" +``` + +Deploy the `tweet-provider` service and its components + +```shell +kubectl apply -f tweet-provider/k8s/state.yaml +kubectl apply -f tweet-provider/k8s/pubsub.yaml +kubectl apply -f tweet-provider/k8s/twitter.yaml +kubectl apply -f tweet-provider/k8s/deployment.yaml +kubectl rollout status deployment/tweet-provider +``` + +If you have changed an existing component, make sure to reload the deployment and wait until the new version is ready + +```shell +kubectl rollout restart deployment/tweet-provider +kubectl rollout status deployment/tweet-provider +``` + +Check Dapr to make sure components were registered correctly + +```shell +kubectl logs -l app=tweet-provider -c daprd --tail 200 +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + +This software is released under the [MIT](../LICENSE) diff --git a/pipeline/tweet-provider/config/pubsub.yaml b/pipeline/tweet-provider/config/pubsub.yaml index 3480cd3..b2903e1 100644 --- a/pipeline/tweet-provider/config/pubsub.yaml +++ b/pipeline/tweet-provider/config/pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: tweeter-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: tweeter-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/pipeline/tweet-provider/config/secret.yaml b/pipeline/tweet-provider/config/secret.yaml index 837a1b1..4ee7cff 100644 --- a/pipeline/tweet-provider/config/secret.yaml +++ b/pipeline/tweet-provider/config/secret.yaml @@ -1,9 +1,9 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: pipeline-secrets -spec: - type: secretstores.local.file - metadata: - - name: secretsFile +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: pipeline-secrets +spec: + type: secretstores.local.file + metadata: + - name: secretsFile value: "/etc/dapr/secrets.json" \ No newline at end of file diff --git a/pipeline/tweet-provider/config/state.yaml b/pipeline/tweet-provider/config/state.yaml index 9f2ba97..ea5bd10 100644 --- a/pipeline/tweet-provider/config/state.yaml +++ b/pipeline/tweet-provider/config/state.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: tweet-store -spec: - type: state.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: tweet-store +spec: + type: state.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/pipeline/tweet-provider/config/twitter.yaml b/pipeline/tweet-provider/config/twitter.yaml index 9b49bc2..16cf05a 100644 --- a/pipeline/tweet-provider/config/twitter.yaml +++ b/pipeline/tweet-provider/config/twitter.yaml @@ -1,23 +1,23 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: tweets -spec: - type: bindings.twitter - metadata: - - name: consumerKey - secretKeyRef: - name: Twitter:ConsumerKey - - name: consumerSecret - secretKeyRef: - name: Twitter:ConsumerSecret - - name: accessToken - secretKeyRef: - name: Twitter:AccessToken - - name: accessSecret - secretKeyRef: - name: Twitter:AccessSecret - - name: query - value: "football" # common term for demo -auth: +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: tweets +spec: + type: bindings.twitter + metadata: + - name: consumerKey + secretKeyRef: + name: Twitter:ConsumerKey + - name: consumerSecret + secretKeyRef: + name: Twitter:ConsumerSecret + - name: accessToken + secretKeyRef: + name: Twitter:AccessToken + - name: accessSecret + secretKeyRef: + name: Twitter:AccessSecret + - name: query + value: "football" # common term for demo +auth: secretStore: pipeline-secrets \ No newline at end of file diff --git a/pipeline/tweet-provider/go.mod b/pipeline/tweet-provider/go.mod index a622884..91d75af 100644 --- a/pipeline/tweet-provider/go.mod +++ b/pipeline/tweet-provider/go.mod @@ -1,11 +1,11 @@ -module github.com/mchmarny/dapr-demos/pipeline/tweet-provider - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect - google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect -) +module github.com/mchmarny/dapr-demos/pipeline/tweet-provider + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect + google.golang.org/genproto v0.0.0-20201002142447-3860012362da // indirect +) diff --git a/pipeline/tweet-provider/go.sum b/pipeline/tweet-provider/go.sum index adb2906..96fb780 100644 --- a/pipeline/tweet-provider/go.sum +++ b/pipeline/tweet-provider/go.sum @@ -1,106 +1,106 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= -google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da h1:DTQYk4u7nICKkkVZsBv0/0po0ChISxAJ5CTAfUhO0PQ= +google.golang.org/genproto v0.0.0-20201002142447-3860012362da/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pipeline/tweet-provider/main.go b/pipeline/tweet-provider/main.go index 9d5b5ab..d68612c 100644 --- a/pipeline/tweet-provider/main.go +++ b/pipeline/tweet-provider/main.go @@ -1,76 +1,76 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "log" - - "net/http" - "os" - "strings" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" - "github.com/pkg/errors" -) - -var ( - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8080") - pubSubName = getEnvVar("PUBSUB_NAME", "tweeter-pubsub") - topicName = getEnvVar("TOPIC_NAME", "tweets") - storeName = getEnvVar("STORE_NAME", "tweet-store") - client dapr.Client -) - -func main() { - // create a Dapr service - s := daprd.NewService(address) - - // create a Dapr client - c, err := dapr.NewClient() - if err != nil { - logger.Fatalf("error creating Dapr client: %v", err) - } - client = c - defer client.Close() - - // add twitter input binding handler - if err := s.AddBindingInvocationHandler("tweets", tweetHandler); err != nil { - logger.Fatalf("error adding binding handler: %v", err) - } - - // start the service - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("error starting service: %v", err) - } -} - -func tweetHandler(ctx context.Context, in *common.BindingEvent) (out []byte, err error) { - logger.Printf("Tweet (query: %s, traceID: %s)", in.Metadata["Query"], in.Metadata["Traceparent"]) - var m map[string]interface{} - if err := json.Unmarshal(in.Data, &m); err != nil { - return nil, errors.Wrap(err, "error deserializing event data") - } - - k := fmt.Sprintf("tw-%s", m["id_str"]) - if err := client.SaveState(ctx, storeName, k, in.Data); err != nil { - return nil, errors.Wrapf(err, "error saving to store:%s with key:%s", storeName, k) - } - - logger.Printf("Tweet saved in store: %s: %s", storeName, k) - if err := client.PublishEvent(ctx, pubSubName, topicName, in.Data); err != nil { - return nil, errors.Wrapf(err, "error publishing to %s/%s", pubSubName, topicName) - } - logger.Printf("Tweet published to %s/%s", pubSubName, topicName) - return -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "net/http" + "os" + "strings" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" + "github.com/pkg/errors" +) + +var ( + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8080") + pubSubName = getEnvVar("PUBSUB_NAME", "tweeter-pubsub") + topicName = getEnvVar("TOPIC_NAME", "tweets") + storeName = getEnvVar("STORE_NAME", "tweet-store") + client dapr.Client +) + +func main() { + // create a Dapr service + s := daprd.NewService(address) + + // create a Dapr client + c, err := dapr.NewClient() + if err != nil { + logger.Fatalf("error creating Dapr client: %v", err) + } + client = c + defer client.Close() + + // add twitter input binding handler + if err := s.AddBindingInvocationHandler("tweets", tweetHandler); err != nil { + logger.Fatalf("error adding binding handler: %v", err) + } + + // start the service + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("error starting service: %v", err) + } +} + +func tweetHandler(ctx context.Context, in *common.BindingEvent) (out []byte, err error) { + logger.Printf("Tweet (query: %s, traceID: %s)", in.Metadata["Query"], in.Metadata["Traceparent"]) + var m map[string]interface{} + if err := json.Unmarshal(in.Data, &m); err != nil { + return nil, errors.Wrap(err, "error deserializing event data") + } + + k := fmt.Sprintf("tw-%s", m["id_str"]) + if err := client.SaveState(ctx, storeName, k, in.Data); err != nil { + return nil, errors.Wrapf(err, "error saving to store:%s with key:%s", storeName, k) + } + + logger.Printf("Tweet saved in store: %s: %s", storeName, k) + if err := client.PublishEvent(ctx, pubSubName, topicName, in.Data); err != nil { + return nil, errors.Wrapf(err, "error publishing to %s/%s", pubSubName, topicName) + } + logger.Printf("Tweet published to %s/%s", pubSubName, topicName) + return +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/pipeline/tweet-viewer/Dockerfile b/pipeline/tweet-viewer/Dockerfile index 5d91fe9..9d36a2d 100755 --- a/pipeline/tweet-viewer/Dockerfile +++ b/pipeline/tweet-viewer/Dockerfile @@ -1,20 +1,20 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ARG APP_VERSION=v0.0.1-default - -ENV APP_VERSION=$APP_VERSION -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -ldflags \ - "-w -extldflags '-static' -X main.AppVersion=${APP_VERSION}" \ - -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . -COPY --from=builder /src/resource ./resource/ - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ARG APP_VERSION=v0.0.1-default + +ENV APP_VERSION=$APP_VERSION +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -ldflags \ + "-w -extldflags '-static' -X main.AppVersion=${APP_VERSION}" \ + -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . +COPY --from=builder /src/resource ./resource/ + +ENTRYPOINT ["./service"] diff --git a/pipeline/tweet-viewer/Makefile b/pipeline/tweet-viewer/Makefile index ee11273..bdabaa8 100644 --- a/pipeline/tweet-viewer/Makefile +++ b/pipeline/tweet-viewer/Makefile @@ -1,58 +1,58 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?=tweet-viewer -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag -all: help - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -debug: tidy ## Runs uncompiled code in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-port 8084 \ - --app-protocol http \ - --dapr-http-port 3500 \ - --components-path ./config \ - --log-level debug \ - go run main.go - -post: ## Posts sample tweet - curl -v -d @tweet.json \ - -H "Content-type: application/json" \ - http://localhost:3500/v1.0/publish/processed-tweets-pubsub/processed-tweets - -image: tidy ## Builds and publish docker image - docker build --build-arg APP_VERSION=$(RELEASE_VERSION) -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -deploy: ## Deploys prebuild image to k8s using currently selected context - kubectl apply -f k8s/source-pubsub.yaml - kubectl apply -f k8s/deployment.yaml - kubectl rollout restart deployment/tweet-viewer - kubectl rollout status deployment/tweet-viewer - -api-patch: ## Patch API gatewaty to add route map - kubectl patch ingress ingress-rules --type json -p "$(cat k8s/ingress.json)" - - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?=tweet-viewer +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: tidy test debug build run jsonevent xmlevent binevent image lint clean tag +all: help + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +debug: tidy ## Runs uncompiled code in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-port 8084 \ + --app-protocol http \ + --dapr-http-port 3500 \ + --components-path ./config \ + --log-level debug \ + go run main.go + +post: ## Posts sample tweet + curl -v -d @tweet.json \ + -H "Content-type: application/json" \ + http://localhost:3500/v1.0/publish/processed-tweets-pubsub/processed-tweets + +image: tidy ## Builds and publish docker image + docker build --build-arg APP_VERSION=$(RELEASE_VERSION) -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +deploy: ## Deploys prebuild image to k8s using currently selected context + kubectl apply -f k8s/source-pubsub.yaml + kubectl apply -f k8s/deployment.yaml + kubectl rollout restart deployment/tweet-viewer + kubectl rollout status deployment/tweet-viewer + +api-patch: ## Patch API gatewaty to add route map + kubectl patch ingress ingress-rules --type json -p "$(cat k8s/ingress.json)" + + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/pipeline/tweet-viewer/config/source-pubsub.yaml b/pipeline/tweet-viewer/config/source-pubsub.yaml index 1ac1bc2..7c549d7 100644 --- a/pipeline/tweet-viewer/config/source-pubsub.yaml +++ b/pipeline/tweet-viewer/config/source-pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: processed-tweets-pubsub -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: processed-tweets-pubsub +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword value: "" \ No newline at end of file diff --git a/pipeline/tweet-viewer/go.mod b/pipeline/tweet-viewer/go.mod index 1c50631..2dcf30c 100644 --- a/pipeline/tweet-viewer/go.mod +++ b/pipeline/tweet-viewer/go.mod @@ -1,10 +1,10 @@ -module github.com/mchmarny/dapr-demos/pipeline/tweet-viewer - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/gorilla/websocket v1.4.2 // indirect - github.com/pkg/errors v0.9.1 - gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 -) +module github.com/mchmarny/dapr-demos/pipeline/tweet-viewer + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/gorilla/websocket v1.4.2 // indirect + github.com/pkg/errors v0.9.1 + gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 +) diff --git a/pipeline/tweet-viewer/go.sum b/pipeline/tweet-viewer/go.sum index 67f51d5..65b9ae6 100644 --- a/pipeline/tweet-viewer/go.sum +++ b/pipeline/tweet-viewer/go.sum @@ -1,111 +1,111 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJcorb+a0hJIQ6uakU5gybjfLWHlXo= -gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJcorb+a0hJIQ6uakU5gybjfLWHlXo= +gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pipeline/tweet-viewer/main.go b/pipeline/tweet-viewer/main.go index 622400b..90c0637 100755 --- a/pipeline/tweet-viewer/main.go +++ b/pipeline/tweet-viewer/main.go @@ -1,115 +1,115 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "html/template" - "log" - "net/http" - "os" - "strings" - - "gopkg.in/olahol/melody.v1" - - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/http" - "github.com/pkg/errors" -) - -var ( - // AppVersion will be overritten during build - AppVersion = "v0.0.1-default" - - // service - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":8084") - pubSubName = getEnvVar("PUBSUB_NAME", "processed-tweets-pubsub") - topicName = getEnvVar("TOPIC_NAME", "processed-tweets") - - broadcaster *melody.Melody - templates *template.Template -) - -func main() { - - // server mux - mux := http.NewServeMux() - - // static content - mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("resource/static")))) - mux.HandleFunc("/favicon.ico", faviconHandler) - - // tempalates - templates = template.Must(template.ParseGlob("resource/template/*")) - - // websocket upgrade - broadcaster = melody.New() - broadcaster.Upgrader.CheckOrigin = func(r *http.Request) bool { return true } - - // other handlers - mux.HandleFunc("/", rootHandler) - mux.HandleFunc("/ws", wsHandler) - - // create a Dapr service - s := daprd.NewServiceWithMux(address, mux) - - // add some topic subscriptions - subscription := &common.Subscription{ - PubsubName: pubSubName, - Topic: topicName, - Route: fmt.Sprintf("/%s", topicName), - } - - if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { - logger.Fatalf("error adding topic subscription: %v", err) - } - - // start the service - if err := s.Start(); err != nil && err != http.ErrServerClosed { - logger.Fatalf("error starting service: %v", err) - } -} - -func wsHandler(w http.ResponseWriter, r *http.Request) { - broadcaster.HandleRequest(w, r) -} - -func rootHandler(w http.ResponseWriter, r *http.Request) { - proto := r.Header.Get("x-forwarded-proto") - if proto == "" { - proto = "http" - } - - data := map[string]string{ - "host": r.Host, - "proto": proto, - "version": AppVersion, - } - - err := templates.ExecuteTemplate(w, "index", data) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} - -func faviconHandler(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, "./resource/static/img/favicon.ico") -} - -func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { - b, err := json.Marshal(e.Data) - if err != nil { - return false, errors.Wrap(err, "error marshaling data") - } - broadcaster.Broadcast(b) - return false, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "encoding/json" + "fmt" + "html/template" + "log" + "net/http" + "os" + "strings" + + "gopkg.in/olahol/melody.v1" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" + "github.com/pkg/errors" +) + +var ( + // AppVersion will be overritten during build + AppVersion = "v0.0.1-default" + + // service + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":8084") + pubSubName = getEnvVar("PUBSUB_NAME", "processed-tweets-pubsub") + topicName = getEnvVar("TOPIC_NAME", "processed-tweets") + + broadcaster *melody.Melody + templates *template.Template +) + +func main() { + + // server mux + mux := http.NewServeMux() + + // static content + mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("resource/static")))) + mux.HandleFunc("/favicon.ico", faviconHandler) + + // tempalates + templates = template.Must(template.ParseGlob("resource/template/*")) + + // websocket upgrade + broadcaster = melody.New() + broadcaster.Upgrader.CheckOrigin = func(r *http.Request) bool { return true } + + // other handlers + mux.HandleFunc("/", rootHandler) + mux.HandleFunc("/ws", wsHandler) + + // create a Dapr service + s := daprd.NewServiceWithMux(address, mux) + + // add some topic subscriptions + subscription := &common.Subscription{ + PubsubName: pubSubName, + Topic: topicName, + Route: fmt.Sprintf("/%s", topicName), + } + + if err := s.AddTopicEventHandler(subscription, eventHandler); err != nil { + logger.Fatalf("error adding topic subscription: %v", err) + } + + // start the service + if err := s.Start(); err != nil && err != http.ErrServerClosed { + logger.Fatalf("error starting service: %v", err) + } +} + +func wsHandler(w http.ResponseWriter, r *http.Request) { + broadcaster.HandleRequest(w, r) +} + +func rootHandler(w http.ResponseWriter, r *http.Request) { + proto := r.Header.Get("x-forwarded-proto") + if proto == "" { + proto = "http" + } + + data := map[string]string{ + "host": r.Host, + "proto": proto, + "version": AppVersion, + } + + err := templates.ExecuteTemplate(w, "index", data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func faviconHandler(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "./resource/static/img/favicon.ico") +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + b, err := json.Marshal(e.Data) + if err != nil { + return false, errors.Wrap(err, "error marshaling data") + } + broadcaster.Broadcast(b) + return false, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +} diff --git a/pipeline/tweet-viewer/resource/static/img/dapr.svg b/pipeline/tweet-viewer/resource/static/img/dapr.svg index 716c3e4..38d1cc0 100644 --- a/pipeline/tweet-viewer/resource/static/img/dapr.svg +++ b/pipeline/tweet-viewer/resource/static/img/dapr.svg @@ -1,15 +1,15 @@ - - - - Artboard - Created with Sketch. - - - - - - - - - + + + + Artboard + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/pipeline/tweet-viewer/resource/static/img/tw.svg b/pipeline/tweet-viewer/resource/static/img/tw.svg index 4d58e50..22cb07e 100644 --- a/pipeline/tweet-viewer/resource/static/img/tw.svg +++ b/pipeline/tweet-viewer/resource/static/img/tw.svg @@ -1,3 +1,3 @@ -" + - "
" + t.user.screen_name + - "" + - "
" + tweetText + "
"; - item.innerHTML = tmsg - appendLog(item); - }; - - } // if log - - +window.onload = function () { + + console.log("Protocol: " + location.protocol); + var wsURL = "ws://" + document.location.host + "/ws" + if (location.protocol == 'https:') { + wsURL = "wss://" + document.location.host + "/ws" + } + console.log("WS URL: " + wsURL); + + var log = document.getElementById("tweets"); + + function appendLog(item) { + var doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1; + log.appendChild(item); + if (doScroll) { + log.scrollTop = log.scrollHeight - log.clientHeight; + } + } + + if (log) { + + sock = new WebSocket(wsURL); + + var connDiv = document.getElementById("connection-status"); + connDiv.innerText = "closed"; + + sock.onopen = function () { + console.log("connected to " + wsURL); + connDiv.innerText = "open"; + }; + + sock.onclose = function (e) { + console.log("connection closed (" + e.code + ")"); + connDiv.innerText = "closed"; + }; + + sock.onmessage = function (e) { + console.log(e); + var t = JSON.parse(e.data); + console.log(t); + + + var scoreStr = "neutral"; + var scoreAlt = "neutral: 0" + if (t.hasOwnProperty("sentiment")) { + console.log(t.sentiment); + if (t.sentiment.sentiment.length > 0) { + scoreStr = t.sentiment.sentiment; + scoreAlt = scoreStr + ": " + t.sentiment.confidence; + } + } + + var tweetText = t.text; + if(t.extended_tweet != null) { + tweetText = t.extended_tweet.full_text; + } + + var item = document.createElement("div"); + item.className = "item"; + // TODO: template this + var tmsg = "" + + "
" + t.user.screen_name + + "" + + "
" + tweetText + "
"; + item.innerHTML = tmsg + appendLog(item); + }; + + } // if log + + }; \ No newline at end of file diff --git a/pipeline/tweet-viewer/resource/template/footer.html b/pipeline/tweet-viewer/resource/template/footer.html index e7461a5..bcce046 100755 --- a/pipeline/tweet-viewer/resource/template/footer.html +++ b/pipeline/tweet-viewer/resource/template/footer.html @@ -1,19 +1,19 @@ -{{ define "footer" }} - - - -
- - - - +{{ define "footer" }} + + + +
+ + + + {{ end }} \ No newline at end of file diff --git a/pipeline/tweet-viewer/resource/template/header.html b/pipeline/tweet-viewer/resource/template/header.html index c0d27e0..5caa217 100755 --- a/pipeline/tweet-viewer/resource/template/header.html +++ b/pipeline/tweet-viewer/resource/template/header.html @@ -1,28 +1,28 @@ -{{ define "header" }} - - - - - dapr event viewer - - - - - - - - - - - -
- - - - +{{ define "header" }} + + + + + dapr event viewer + + + + + + + + + + + +
+ + + + {{end}} \ No newline at end of file diff --git a/pipeline/tweet-viewer/resource/template/index.html b/pipeline/tweet-viewer/resource/template/index.html index 5fc3db0..655823a 100755 --- a/pipeline/tweet-viewer/resource/template/index.html +++ b/pipeline/tweet-viewer/resource/template/index.html @@ -1,12 +1,12 @@ -{{ define "index" }} - -{{ template "header" . }} - - -
-
-
- -{{ template "footer" . }} - +{{ define "index" }} + +{{ template "header" . }} + + +
+
+
+ +{{ template "footer" . }} + {{ end }} \ No newline at end of file diff --git a/pipeline/tweet-viewer/tweet.json b/pipeline/tweet-viewer/tweet.json index 1f52950..b490ef2 100644 --- a/pipeline/tweet-viewer/tweet.json +++ b/pipeline/tweet-viewer/tweet.json @@ -1,107 +1,107 @@ -{ - "created_at": "Sun Feb 25 18:11:01 +0000 2018", - "id": 967824267948773377, - "id_str": "967824267948773377", - "text": "From pilot to astronaut, Robert H. Lawrence was the first African-American to be selected as an astronaut by any na… https://t.co/FjPEWnh804", - "truncated": false, - "entities": { - "hashtags": [], - "symbols": [], - "user_mentions": [], - "urls": [ - { - "url": "https://t.co/FjPEWnh804", - "expanded_url": "https://twitter.com/i/web/status/967824267948773377", - "display_url": "twitter.com/i/web/status/9…", - "indices": [ - 117, - 140 - ] - } - ] - }, - "metadata": { - "result_type": "popular", - "iso_language_code": "en" - }, - "user": { - "id": 11348282, - "id_str": "11348282", - "name": "NASA", - "screen_name": "NASA", - "location": "", - "description": "Explore the universe and discover our home planet with @NASA. We usually post in EST (UTC-5)", - "url": "https://t.co/TcEE6NS8nD", - "entities": { - "url": { - "urls": [ - { - "url": "https://t.co/TcEE6NS8nD", - "expanded_url": "http://www.nasa.gov", - "display_url": "nasa.gov", - "indices": [ - 0, - 23 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 28605561, - "friends_count": 270, - "listed_count": 90405, - "created_at": "Wed Dec 19 20:20:32 +0000 2007", - "favourites_count": 2960, - "utc_offset": -18000, - "time_zone": "Eastern Time (US & Canada)", - "geo_enabled": false, - "verified": true, - "statuses_count": 50713, - "lang": "en", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "000000", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/590922434682880000/3byPYvqe.jpg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/590922434682880000/3byPYvqe.jpg", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/188302352/nasalogo_twitter_normal.jpg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/188302352/nasalogo_twitter_normal.jpg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/11348282/1518798395", - "profile_link_color": "205BA7", - "profile_sidebar_border_color": "000000", - "profile_sidebar_fill_color": "F3F2F2", - "profile_text_color": "000000", - "profile_use_background_image": true, - "has_extended_profile": true, - "default_profile": false, - "default_profile_image": false, - "following": null, - "follow_request_sent": null, - "notifications": null, - "translator_type": "regular" - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "is_quote_status": false, - "retweet_count": 988, - "favorite_count": 3875, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "en", - "sentiment": { - "sentiment": "positive", - "confidence": 0.98 - } -} - - - - +{ + "created_at": "Sun Feb 25 18:11:01 +0000 2018", + "id": 967824267948773377, + "id_str": "967824267948773377", + "text": "From pilot to astronaut, Robert H. Lawrence was the first African-American to be selected as an astronaut by any na… https://t.co/FjPEWnh804", + "truncated": false, + "entities": { + "hashtags": [], + "symbols": [], + "user_mentions": [], + "urls": [ + { + "url": "https://t.co/FjPEWnh804", + "expanded_url": "https://twitter.com/i/web/status/967824267948773377", + "display_url": "twitter.com/i/web/status/9…", + "indices": [ + 117, + 140 + ] + } + ] + }, + "metadata": { + "result_type": "popular", + "iso_language_code": "en" + }, + "user": { + "id": 11348282, + "id_str": "11348282", + "name": "NASA", + "screen_name": "NASA", + "location": "", + "description": "Explore the universe and discover our home planet with @NASA. We usually post in EST (UTC-5)", + "url": "https://t.co/TcEE6NS8nD", + "entities": { + "url": { + "urls": [ + { + "url": "https://t.co/TcEE6NS8nD", + "expanded_url": "http://www.nasa.gov", + "display_url": "nasa.gov", + "indices": [ + 0, + 23 + ] + } + ] + }, + "description": { + "urls": [] + } + }, + "protected": false, + "followers_count": 28605561, + "friends_count": 270, + "listed_count": 90405, + "created_at": "Wed Dec 19 20:20:32 +0000 2007", + "favourites_count": 2960, + "utc_offset": -18000, + "time_zone": "Eastern Time (US & Canada)", + "geo_enabled": false, + "verified": true, + "statuses_count": 50713, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "is_translation_enabled": false, + "profile_background_color": "000000", + "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/590922434682880000/3byPYvqe.jpg", + "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/590922434682880000/3byPYvqe.jpg", + "profile_background_tile": false, + "profile_image_url": "http://pbs.twimg.com/profile_images/188302352/nasalogo_twitter_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/188302352/nasalogo_twitter_normal.jpg", + "profile_banner_url": "https://pbs.twimg.com/profile_banners/11348282/1518798395", + "profile_link_color": "205BA7", + "profile_sidebar_border_color": "000000", + "profile_sidebar_fill_color": "F3F2F2", + "profile_text_color": "000000", + "profile_use_background_image": true, + "has_extended_profile": true, + "default_profile": false, + "default_profile_image": false, + "following": null, + "follow_request_sent": null, + "notifications": null, + "translator_type": "regular" + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "retweet_count": 988, + "favorite_count": 3875, + "favorited": false, + "retweeted": false, + "possibly_sensitive": false, + "lang": "en", + "sentiment": { + "sentiment": "positive", + "confidence": 0.98 + } +} + + + + diff --git a/setup/Makefile b/setup/Makefile index 133dfd8..9c7c818 100644 --- a/setup/Makefile +++ b/setup/Makefile @@ -1,310 +1,310 @@ -DOMAIN ?=example.com -CLUSTER_NAME ?=demo -DAPR_RELEASE ?=1.0.0-rc.1 -DAPR_HA ?=true -DAPR_LOG_AS_JSON ?=true - -.PHONY: all -all: - @echo === ACTIVE CONFIGURATION === - @echo "DOMAIN: ${DOMAIN}" - @echo "CLUSTER_NAME: ${CLUSTER_NAME}" - @echo "DAPR_RELEASE: ${DAPR_RELEASE}" - @echo "DAPR_HA: ${DAPR_HA}" - @echo "DAPR_LOG_AS_JSON: ${DAPR_LOG_AS_JSON}" - @echo - @echo "Export these as environment variables to change their values" - @echo - -.PHONY: node-list -node-list: ## Print node resources and their usage - kubectl top nodes - -.PHONY: certs -certs: ## Create wildcard TLS certificates using letsencrypt - mkdir -p certs/$(DOMAIN) - sudo certbot certonly --manual --preferred-challenges dns -d "*.$(DOMAIN)" - sudo cp "/etc/letsencrypt/live/$(DOMAIN)/fullchain.pem" certs/$(DOMAIN)/cert-ca.pem - sudo cp "/etc/letsencrypt/live/$(DOMAIN)/privkey.pem" certs/$(DOMAIN)/cert-pk.pem - sudo chmod 644 certs/$(DOMAIN)/*.pem - -.PHONY: dapr -dapr: dapr-install keda-install observe-install ## Install dapr, keda, and observability - -.PHONY: dapr-install -dapr-install: ## Install and configures Dapr - # Updating Help repos... - helm repo add dapr https://dapr.github.io/helm-charts/ - helm repo update - # Installing Dapr... - kubectl create ns dapr-system - helm install dapr dapr/dapr -n dapr-system \ - --version $(DAPR_RELEASE) \ - --set global.logAsJson=$(DAPR_LOG_AS_JSON) \ - --set global.ha.enabled=$(DAPR_HA) - # Wait for everything to finish installing - kubectl rollout status deployment/dapr-operator -n dapr-system - kubectl rollout status deployment/dapr-dashboard -n dapr-system - kubectl rollout status deployment/dapr-sentry -n dapr-system - kubectl rollout status deployment/dapr-sidecar-injector -n dapr-system - -.PHONY: keda-install -keda-install: ## Install and configures Keda - # Updating Help repos... - helm repo add kedacore https://kedacore.github.io/charts - helm repo update - # Installing Keda - kubectl create ns keda - helm install keda kedacore/keda -n keda --set logLevel=debug - # Wait for everything to finish installing - kubectl rollout status deployment/keda-operator -n keda - kubectl rollout status deployment/keda-operator-metrics-apiserver -n keda - -.PHONY: observe-install -observe-install: ## Install observability stack - # Updating Help repos... - helm repo add stable https://charts.helm.sh/stable - helm repo add elastic https://helm.elastic.co - helm repo update - # Installing observabiliity... - kubectl create ns dapr-monitoring - kubectl apply -f config/fluentd-config.yaml -f config/fluentd.yaml - kubectl apply -f config/zipkin.yaml -n dapr-monitoring - helm install elasticsearch elastic/elasticsearch -n dapr-monitoring - helm install dapr-prom stable/prometheus -n dapr-monitoring - helm install grafana stable/grafana -n dapr-monitoring \ - --set persistence.enabled=true \ - --set persistence.accessModes={ReadWriteOnce} \ - --set persistence.size=8Gi - helm install kibana elastic/kibana -n dapr-monitoring - # Wait for everything to be ready... - kubectl rollout status deployment/dapr-prom-kube-state-metrics -n dapr-monitoring - kubectl rollout status deployment/dapr-prom-prometheus-alertmanager -n dapr-monitoring - kubectl rollout status deployment/dapr-prom-prometheus-pushgateway -n dapr-monitoring - kubectl rollout status deployment/dapr-prom-prometheus-server -n dapr-monitoring - kubectl rollout status deployment/grafana -n dapr-monitoring - kubectl rollout status deployment/kibana-kibana -n dapr-monitoring - -.PHONY: config -config: ports ## Configure Dapr after install - $(eval GRAFANA_PASS=$(shell kubectl get secret -n dapr-monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode)) - # Check that everything is ready... - kubectl rollout status deployment/dapr-prom-kube-state-metrics -n dapr-monitoring - kubectl rollout status deployment/dapr-prom-prometheus-alertmanager -n dapr-monitoring - kubectl rollout status deployment/dapr-prom-prometheus-pushgateway -n dapr-monitoring - kubectl rollout status deployment/dapr-prom-prometheus-server -n dapr-monitoring - kubectl rollout status deployment/grafana -n dapr-monitoring - kubectl rollout status deployment/kibana-kibana -n dapr-monitoring - # Configure grafana - curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ - -H "Content-Type: application/json" \ - -d '{ "name":"Dapr", "type":"prometheus", "url":"http://dapr-prom-prometheus-server.dapr-monitoring", "access":"proxy", "basicAuth":false }' \ - http://localhost:8888/api/datasources - curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ - -H "Content-Type: application/json" \ - -d @config/system-services-dashboard.json \ - http://localhost:8888/api/dashboards/db - curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ - -H "Content-Type: application/json" \ - -d @config/sidecar-dashboard.json \ - http://localhost:8888/api/dashboards/db - curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ - -H "Content-Type: application/json" \ - -d @config/actor-dashboard.json \ - http://localhost:8888/api/dashboards/db - # Configure kibana - curl -X POST -H "kbn-xsrf: true" \ - -H "Content-Type: application/json" \ - -d '{"attributes":{"title":"dapr*","timeFieldName":"@timestamp"}}' \ - "http://localhost:5601/api/saved_objects/index-pattern/dapr" - curl -X POST -H "kbn-xsrf: true" \ - -H "Content-Type: application/json" \ - -d '{"value":"dapr"}' \ - "http://localhost:5601/api/kibana/settings/defaultIndex" - -.PHONY: ingress -ingress: ## Install and configures Ngnx ingress, configure SSL termination, Dapr API auth - # Updating Help repos... - helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx - helm repo update - # Configure nginx namespace - sed "s/NSNAME/nginx/g" config/namespace-template.yml > config/namespace.yml - kubectl apply -f config/namespace.yml - # Gen Dapr API token - $(eval API_TOKEN=$(shell openssl rand -base64 32)) - kubectl create secret generic dapr-api-token --from-literal=token="$(API_TOKEN)" -n nginx - # Apply ingress config - kubectl apply -f config/ingress-config.yaml -n nginx - # Deploy nginx... - helm install nginx ingress-nginx/ingress-nginx \ - --set controller.replicaCount=2 \ - --set controller.metrics.enabled=true \ - -f config/ingress-annotations.yaml \ - -n nginx - kubectl rollout status deployment/nginx-ingress-nginx-controller -n nginx - # Install cert secrets - kubectl create secret tls tls-secret \ - --key certs/$(DOMAIN)/cert-pk.pem \ - --cert certs/$(DOMAIN)/cert-ca.pem \ - -n nginx - sed "s/DOMAINNAME/${DOMAIN}/g" config/ingress-template.yaml > config/ingress.yaml - # Apply configured ingress - kubectl apply -f config/ingress.yaml -n nginx - -.PHONY: dns -dns: ## Check DNS resolution for cluster IP - dig api.$(DOMAIN) - $(eval LB_IP=$(shell kubectl get svc nginx-ingress-nginx-controller -n nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')) - @echo === DNS CHECK === - @echo - @echo "Ensure the A record for 'api.${DOMAIN}' in the ANSWER SECTION resolves to:" - @echo - @echo " ${LB_IP}" - @echo - @echo If not, update DNS with below entry and re-run this test before moving to the next step - @echo - @echo " Hostname: *" - @echo " IP address: ${LB_IP}" - @echo " TTL: 1m" - @echo - -.PHONY: test -test: ## Test deployment and execute Dapr API health checks - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode)) - curl -v \ - -H "Content-type: application/json" \ - -H "dapr-api-token: $(API_TOKEN)" \ - "https://api.$(DOMAIN)/v1.0/healthz" - @echo - @echo === DNS CHECK === - @echo Ensure server certificate has: - @echo - @echo " subject: CN=*.${DOMAIN}" - @echo " subjectAltName: host 'api.${DOMAIN}' matched cert's '*.${DOMAIN}'" - @echo " SSL certificate verify ok" - @echo - @echo And that the response status from Dapr health checks was '200' - @echo - @echo " HTTP/2 200" - @echo - -.PHONY: token -token: ## Print Dapr API token - $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode)) - @echo - @echo Dapr API token is: - @echo - @echo " ${API_TOKEN}" - @echo - -.PHONY: pass -pass: ## Print Grafana admin password - $(eval GPASS=$(shell kubectl get secret -n dapr-monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode)) - @echo - @echo Grafana admin password is: - @echo - @echo " ${GPASS}" - @echo - -.PHONY: ports -ports: portstop ## Forward observability ports - kubectl port-forward svc/kibana-kibana 5601 -n dapr-monitoring & - kubectl port-forward svc/grafana 8888:80 -n dapr-monitoring & - kubectl port-forward svc/zipkin 9411 -n dapr-monitoring & - @echo Ports forwarded: - @echo - @echo kibana - http://localhost:5601 - @echo grafana - http://localhost:8888 - @echo zipkin - http://localhost:9411 - @echo - @echo "To stop forwarding run 'make portstop'" - @echo - -.PHONY: reload -reload: ## Reloads API to pickup new components - kubectl rollout restart deployment/nginx-ingress-nginx-controller -n nginx - kubectl rollout status deployment/nginx-ingress-nginx-controller -n nginx - -.PHONY: redis -redis: ## Install Redis into the cluster - # Updating Help repos... - helm repo add bitnami https://charts.bitnami.com/bitnami - helm repo update - kubectl create ns redis - # redis - helm install redis bitnami/redis -n redis - # Waiting for redis to be ready... - kubectl rollout status statefulset.apps/redis-master -n redis - kubectl rollout status statefulset.apps/redis-slave -n redis - -.PHONY: mongo -mongo: ## Install Mongo into the cluster - # Updating Help repos... - helm repo add bitnami https://charts.bitnami.com/bitnami - helm repo update - kubectl create ns mongo - # mongo - helm install mongo \ - --set architecture=replicaset \ - --set auth.username=dapr \ - --set auth.database=dapr \ - --set replicaSetName=staters0 \ - --set replicaCount=3 \ - bitnami/mongodb \ - -n mongo - # Waiting for mongo to be ready... - kubectl rollout status statefulset.apps/mongo-mongodb -n mongo - kubectl rollout status statefulset.apps/mongo-mongodb-arbiter -n mongo - -.PHONY: kafka -kafka: ## Install Kafka into the cluster - # Updating Help repos... - helm repo add confluentinc https://confluentinc.github.io/cp-helm-charts/ - helm repo update - kubectl create ns kafka - # kafka - helm install kafka confluentinc/cp-helm-charts -n kafka \ - --set cp-schema-registry.enabled=false \ - --set cp-kafka-rest.enabled=false \ - --set cp-kafka-connect.enabled=false - # wait for the deployment - kubectl rollout status deployment.apps/kafka-cp-control-center -n kafka - kubectl rollout status deployment.apps/kafka-cp-ksql-server -n kafka - kubectl rollout status statefulset.apps/kafka-cp-kafka -n kafka - kubectl rollout status statefulset.apps/kafka-cp-zookeeper -n kafka - -.PHONY: namespace -namespace: ## Configures namespace (make namespace NSNAME=default) - # Create namespace if one doesn't exists - sed "s/NSNAME/${NSNAME}/g" config/namespace-template.yml > config/namespace.yml - kubectl apply -f config/namespace.yml - -.PHONY: namespace-pass -namespace-pass: ## Configures Mongo and Redis passwords in namespace (make namespace-pass NSNAME=default) - # Configure Redis password - $(eval REDIS_PASSWORD=$(shell kubectl get secret -n redis redis -o jsonpath="{.data.redis-password}" | base64 --decode)) - kubectl create secret generic redis-secret --from-literal=password="$(REDIS_PASSWORD)" -n $(NSNAME) - # Configre Mongo password - $(eval MONGO_PASSWORD=$(shell kubectl get secret -n mongo mongo-mongodb -o jsonpath="{.data.mongodb-password}" | base64 --decode)) - kubectl create secret generic mongo-secret --from-literal=password="$(MONGO_PASSWORD)" -n $(NSNAME) - -.PHONY: portstop -portstop: ## Stop previously forwarded observability ports - if pgrep kubectl &> /dev/null ; then pkill kubectl -9 ; fi - -.PHONY: upgrade -upgrade: ## Upgrades Dapr to specific release version (make upgrade DAPR_RELEASE="0.11.0-rc.3") - kubectl delete clusterrolebinding dapr-operator - dapr mtls export -o ./certs - helm upgrade dapr -n=dapr-system \ - --set-string global.tag=$(DAPR_RELEASE) \ - --set-string global.registry=docker.io/daprio \ - --set-file dapr_sentry.tls.root.certPEM=./certs/ca.crt \ - --set-file dapr_sentry.tls.issuer.certPEM=./certs/issuer.crt \ - --set-file dapr_sentry.tls.issuer.keyPEM=./certs/issuer.key \ - --reset-values ./charts/dapr - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +DOMAIN ?=mfussell.com +CLUSTER_NAME ?=mfusselldemo10 +DAPR_RELEASE ?=1.0.0-rc.2 +DAPR_HA ?=true +DAPR_LOG_AS_JSON ?=true + +.PHONY: all +all: + @echo === ACTIVE CONFIGURATION === + @echo "DOMAIN: ${DOMAIN}" + @echo "CLUSTER_NAME: ${CLUSTER_NAME}" + @echo "DAPR_RELEASE: ${DAPR_RELEASE}" + @echo "DAPR_HA: ${DAPR_HA}" + @echo "DAPR_LOG_AS_JSON: ${DAPR_LOG_AS_JSON}" + @echo + @echo "Export these as environment variables to change their values" + @echo + +.PHONY: node-list +node-list: ## Print node resources and their usage + kubectl top nodes + +.PHONY: certs +certs: ## Create wildcard TLS certificates using letsencrypt + mkdir -p certs/$(DOMAIN) + sudo certbot certonly --manual --preferred-challenges dns -d "*.$(DOMAIN)" + sudo cp "/etc/letsencrypt/live/$(DOMAIN)/fullchain.pem" certs/$(DOMAIN)/cert-ca.pem + sudo cp "/etc/letsencrypt/live/$(DOMAIN)/privkey.pem" certs/$(DOMAIN)/cert-pk.pem + sudo chmod 644 certs/$(DOMAIN)/*.pem + +.PHONY: dapr +dapr: dapr-install keda-install observe-install ## Install dapr, keda, and observability + +.PHONY: dapr-install +dapr-install: ## Install and configures Dapr + # Updating Help repos... + helm repo add dapr https://dapr.github.io/helm-charts/ + helm repo update + # Installing Dapr... + kubectl create ns dapr-system + helm install dapr dapr/dapr -n dapr-system \ + --version $(DAPR_RELEASE) \ + --set global.logAsJson=$(DAPR_LOG_AS_JSON) \ + --set global.ha.enabled=$(DAPR_HA) + # Wait for everything to finish installing + kubectl rollout status deployment/dapr-operator -n dapr-system + kubectl rollout status deployment/dapr-dashboard -n dapr-system + kubectl rollout status deployment/dapr-sentry -n dapr-system + kubectl rollout status deployment/dapr-sidecar-injector -n dapr-system + +.PHONY: keda-install +keda-install: ## Install and configures Keda + # Updating Help repos... + helm repo add kedacore https://kedacore.github.io/charts + helm repo update + # Installing Keda + kubectl create ns keda + helm install keda kedacore/keda -n keda --set logLevel=debug + # Wait for everything to finish installing + kubectl rollout status deployment/keda-operator -n keda + kubectl rollout status deployment/keda-operator-metrics-apiserver -n keda + +.PHONY: observe-install +observe-install: ## Install observability stack + # Updating Help repos... + helm repo add stable https://charts.helm.sh/stable + helm repo add elastic https://helm.elastic.co + helm repo update + # Installing observabiliity... + kubectl create ns dapr-monitoring + kubectl apply -f config/fluentd-config.yaml -f config/fluentd.yaml + kubectl apply -f config/zipkin.yaml -n dapr-monitoring + helm install elasticsearch elastic/elasticsearch -n dapr-monitoring + helm install dapr-prom stable/prometheus -n dapr-monitoring + helm install grafana stable/grafana -n dapr-monitoring \ + --set persistence.enabled=true \ + --set persistence.accessModes={ReadWriteOnce} \ + --set persistence.size=8Gi + helm install kibana elastic/kibana -n dapr-monitoring + # Wait for everything to be ready... + kubectl rollout status deployment/dapr-prom-kube-state-metrics -n dapr-monitoring + kubectl rollout status deployment/dapr-prom-prometheus-alertmanager -n dapr-monitoring + kubectl rollout status deployment/dapr-prom-prometheus-pushgateway -n dapr-monitoring + kubectl rollout status deployment/dapr-prom-prometheus-server -n dapr-monitoring + kubectl rollout status deployment/grafana -n dapr-monitoring + kubectl rollout status deployment/kibana-kibana -n dapr-monitoring + +.PHONY: config +config: ports ## Configure Dapr after install + $(eval GRAFANA_PASS=$(shell kubectl get secret -n dapr-monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode)) + # Check that everything is ready... + kubectl rollout status deployment/dapr-prom-kube-state-metrics -n dapr-monitoring + kubectl rollout status deployment/dapr-prom-prometheus-alertmanager -n dapr-monitoring + kubectl rollout status deployment/dapr-prom-prometheus-pushgateway -n dapr-monitoring + kubectl rollout status deployment/dapr-prom-prometheus-server -n dapr-monitoring + kubectl rollout status deployment/grafana -n dapr-monitoring + kubectl rollout status deployment/kibana-kibana -n dapr-monitoring + # Configure grafana + curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ + -H "Content-Type: application/json" \ + -d '{ "name":"Dapr", "type":"prometheus", "url":"http://dapr-prom-prometheus-server.dapr-monitoring", "access":"proxy", "basicAuth":false }' \ + http://localhost:8888/api/datasources + curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ + -H "Content-Type: application/json" \ + -d @config/system-services-dashboard.json \ + http://localhost:8888/api/dashboards/db + curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ + -H "Content-Type: application/json" \ + -d @config/sidecar-dashboard.json \ + http://localhost:8888/api/dashboards/db + curl -X POST -s -k -u "admin:$(GRAFANA_PASS)" \ + -H "Content-Type: application/json" \ + -d @config/actor-dashboard.json \ + http://localhost:8888/api/dashboards/db + # Configure kibana + curl -X POST -H "kbn-xsrf: true" \ + -H "Content-Type: application/json" \ + -d '{"attributes":{"title":"dapr*","timeFieldName":"@timestamp"}}' \ + "http://localhost:5601/api/saved_objects/index-pattern/dapr" + curl -X POST -H "kbn-xsrf: true" \ + -H "Content-Type: application/json" \ + -d '{"value":"dapr"}' \ + "http://localhost:5601/api/kibana/settings/defaultIndex" + +.PHONY: ingress +ingress: ## Install and configures Ngnx ingress, configure SSL termination, Dapr API auth + # Updating Help repos... + helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx + helm repo update + # Configure nginx namespace + sed "s/NSNAME/nginx/g" config/namespace-template.yml > config/namespace.yml + kubectl apply -f config/namespace.yml + # Gen Dapr API token + $(eval API_TOKEN=$(shell openssl rand -base64 32)) + kubectl create secret generic dapr-api-token --from-literal=token="$(API_TOKEN)" -n nginx + # Apply ingress config + kubectl apply -f config/ingress-config.yaml -n nginx + # Deploy nginx... + helm install nginx ingress-nginx/ingress-nginx \ + --set controller.replicaCount=2 \ + --set controller.metrics.enabled=true \ + -f config/ingress-annotations.yaml \ + -n nginx + kubectl rollout status deployment/nginx-ingress-nginx-controller -n nginx + # Install cert secrets + kubectl create secret tls tls-secret \ + --key certs/$(DOMAIN)/cert-pk.pem \ + --cert certs/$(DOMAIN)/cert-ca.pem \ + -n nginx + sed "s/DOMAINNAME/${DOMAIN}/g" config/ingress-template.yaml > config/ingress.yaml + # Apply configured ingress + kubectl apply -f config/ingress.yaml -n nginx + +.PHONY: dns +dns: ## Check DNS resolution for cluster IP + dig api.$(DOMAIN) + $(eval LB_IP=$(shell kubectl get svc nginx-ingress-nginx-controller -n nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')) + @echo === DNS CHECK === + @echo + @echo "Ensure the A record for 'api.${DOMAIN}' in the ANSWER SECTION resolves to:" + @echo + @echo " ${LB_IP}" + @echo + @echo If not, update DNS with below entry and re-run this test before moving to the next step + @echo + @echo " Hostname: *" + @echo " IP address: ${LB_IP}" + @echo " TTL: 1m" + @echo + +.PHONY: test +test: ## Test deployment and execute Dapr API health checks + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode)) + curl -v \ + -H "Content-type: application/json" \ + -H "dapr-api-token: $(API_TOKEN)" \ + "https://api.$(DOMAIN)/v1.0/healthz" + @echo + @echo === DNS CHECK === + @echo Ensure server certificate has: + @echo + @echo " subject: CN=*.${DOMAIN}" + @echo " subjectAltName: host 'api.${DOMAIN}' matched cert's '*.${DOMAIN}'" + @echo " SSL certificate verify ok" + @echo + @echo And that the response status from Dapr health checks was '200' + @echo + @echo " HTTP/2 200" + @echo + +.PHONY: token +token: ## Print Dapr API token + $(eval API_TOKEN=$(shell kubectl get secret dapr-api-token -n nginx -o jsonpath="{.data.token}" | base64 --decode)) + @echo + @echo Dapr API token is: + @echo + @echo " ${API_TOKEN}" + @echo + +.PHONY: pass +pass: ## Print Grafana admin password + $(eval GPASS=$(shell kubectl get secret -n dapr-monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode)) + @echo + @echo Grafana admin password is: + @echo + @echo " ${GPASS}" + @echo + +.PHONY: ports +ports: portstop ## Forward observability ports + kubectl port-forward svc/kibana-kibana 5601 -n dapr-monitoring & + kubectl port-forward svc/grafana 8888:80 -n dapr-monitoring & + kubectl port-forward svc/zipkin 9411 -n dapr-monitoring & + @echo Ports forwarded: + @echo + @echo kibana - http://localhost:5601 + @echo grafana - http://localhost:8888 + @echo zipkin - http://localhost:9411 + @echo + @echo "To stop forwarding run 'make portstop'" + @echo + +.PHONY: reload +reload: ## Reloads API to pickup new components + kubectl rollout restart deployment/nginx-ingress-nginx-controller -n nginx + kubectl rollout status deployment/nginx-ingress-nginx-controller -n nginx + +.PHONY: redis +redis: ## Install Redis into the cluster + # Updating Help repos... + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo update + kubectl create ns redis + # redis + helm install redis bitnami/redis -n redis + # Waiting for redis to be ready... + kubectl rollout status statefulset.apps/redis-master -n redis + kubectl rollout status statefulset.apps/redis-slave -n redis + +.PHONY: mongo +mongo: ## Install Mongo into the cluster + # Updating Help repos... + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo update + kubectl create ns mongo + # mongo + helm install mongo \ + --set architecture=replicaset \ + --set auth.username=dapr \ + --set auth.database=dapr \ + --set replicaSetName=staters0 \ + --set replicaCount=3 \ + bitnami/mongodb \ + -n mongo + # Waiting for mongo to be ready... + kubectl rollout status statefulset.apps/mongo-mongodb -n mongo + kubectl rollout status statefulset.apps/mongo-mongodb-arbiter -n mongo + +.PHONY: kafka +kafka: ## Install Kafka into the cluster + # Updating Help repos... + helm repo add confluentinc https://confluentinc.github.io/cp-helm-charts/ + helm repo update + kubectl create ns kafka + # kafka + helm install kafka confluentinc/cp-helm-charts -n kafka \ + --set cp-schema-registry.enabled=false \ + --set cp-kafka-rest.enabled=false \ + --set cp-kafka-connect.enabled=false + # wait for the deployment + kubectl rollout status deployment.apps/kafka-cp-control-center -n kafka + kubectl rollout status deployment.apps/kafka-cp-ksql-server -n kafka + kubectl rollout status statefulset.apps/kafka-cp-kafka -n kafka + kubectl rollout status statefulset.apps/kafka-cp-zookeeper -n kafka + +.PHONY: namespace +namespace: ## Configures namespace (make namespace NSNAME=default) + # Create namespace if one doesn't exists + sed "s/NSNAME/${NSNAME}/g" config/namespace-template.yml > config/namespace.yml + kubectl apply -f config/namespace.yml + +.PHONY: namespace-pass +namespace-pass: ## Configures Mongo and Redis passwords in namespace (make namespace-pass NSNAME=default) + # Configure Redis password + $(eval REDIS_PASSWORD=$(shell kubectl get secret -n redis redis -o jsonpath="{.data.redis-password}" | base64 --decode)) + kubectl create secret generic redis-secret --from-literal=password="$(REDIS_PASSWORD)" -n $(NSNAME) + # Configre Mongo password + $(eval MONGO_PASSWORD=$(shell kubectl get secret -n mongo mongo-mongodb -o jsonpath="{.data.mongodb-password}" | base64 --decode)) + kubectl create secret generic mongo-secret --from-literal=password="$(MONGO_PASSWORD)" -n $(NSNAME) + +.PHONY: portstop +portstop: ## Stop previously forwarded observability ports + if pgrep kubectl &> /dev/null ; then pkill kubectl -9 ; fi + +.PHONY: upgrade +upgrade: ## Upgrades Dapr to specific release version (make upgrade DAPR_RELEASE="0.11.0-rc.3") + kubectl delete clusterrolebinding dapr-operator + dapr mtls export -o ./certs + helm upgrade dapr -n=dapr-system \ + --set-string global.tag=$(DAPR_RELEASE) \ + --set-string global.registry=docker.io/daprio \ + --set-file dapr_sentry.tls.root.certPEM=./certs/ca.crt \ + --set-file dapr_sentry.tls.issuer.certPEM=./certs/issuer.crt \ + --set-file dapr_sentry.tls.issuer.keyPEM=./certs/issuer.key \ + --reset-values ./charts/dapr + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/setup/README.md b/setup/README.md index 5accd7d..60b4066 100644 --- a/setup/README.md +++ b/setup/README.md @@ -1,117 +1,117 @@ -# Dapr Cluster Setup - -An opinionated deployment of Dapr on Kubernetes, configured with: - -* Ingress with custom domain and TLS termination - * [NGINX](https://nginx.org/en/) for ingress controller and TLS to service mapping - * [letsencrypt](https://letsencrypt.org/) as certificate provider -* [KEDA](https://keda.sh/) for autoscaling -* Metrics Monitoring - * [Prometheus](https://prometheus.io/) for metrics aggregation - * [Grafana](https://grafana.com/) for metrics visualization with Dapr monitoring dashboards -* Log Management - * [Fluentd](https://www.fluentd.org/) for log collection and forwarding - * [Elasticsearch](https://www.elastic.co/) for log aggregation and query execution - * [Kibana](https://www.elastic.co/products/kibana) for full-text log query and visualization -* Distributed Tracing - * [Jaeger](https://www.jaegertracing.io/) for capturing traces, latency and dependency viewing - -> All demos in the [dapr-demo](../) repository are validated on this deployment - -## Prerequisites - -* 1.15+ Kubernates cluster. If needed, you can setup cluster on: - * [AKS](./aks/) - * [GKE](./gke/) - * AKS (coming) -* Tooling on the machine where you will be running this setup: - * [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) to do k8s stuff (`brew install kubectl`) - * [Helm 3](https://helm.sh/docs/intro/install/) to install Dapr and its dependencies (`brew install helm`) - * [certbot](https://certbot.eff.org/lets-encrypt/osx-other.html) to generate wildcard cert (`brew install certbot`) -* Domain name and access to the DNS service where you can manage that domain (required for letsencrypt challenge during cert generation and the `A` record creation to pont to the ingress gateway IP for custom domain support) - -## Setup - -The following parameters can be used to configure your deployment. Define these as environment variables to set or override the default value: - -```shell -DOMAIN # default: example.com -DAPR_HA # default: true -DAPR_LOG_AS_JSON # default: true -``` - -> Note, make sure the correct "target" cluster is set kubectl context (`kubectl config current-context`). You can lists all registered contexts using: `kubectl config get-contexts`, and if needed, set it using `kubectl config use-context demo`. - -## Usage - -Start by navigate to the [setup](./setup) directory - -> Run `make` by itself to see the active configuration - -To deploy and configure Dapr - -* `make dapr` to install Dapr, KEDA, and the entire observability stack -* `make config` to perform post-install configurations - -> Optionally you can use `make upgrade` to in place upgrade Dapr to specific version - -To configure external access - -* `make ip` (optional) to create static IP in the cluster resource group -* `make certs` to create TLS certs using letsencrypt -* `make ingress` to configures NGINX ingress, SSL termination, Dapr API auth -* `make dns` to configure your DNS service for custom domain support -* `make test` to test deployment - -To deploy in-cluster data services - -* `make redis` to install Redis into the cluster -* `make mongo` to install Mongo into the cluster -* `make kafka` to install Kafka into the cluster - -And few cluster operations helpers - -* `node-list` to print node resources and their usage -* `make ports` to forward observability dashboards ports -* `make pass` to print the Grafana password (username: admin) - -Then for each namespace you want to deploy Dapr apps to - -* `make namespace` to create/configure namespace with service secrets - -## Accessing observability dashboards - -To get access to the Kibana, Grafana, Zipkin dashboards run: - -```shell -make ports -``` - -This will forward the necessary ports so you can access the dashboards using: - -* kibana - http://localhost:5601 -* grafana - http://localhost:8888 -* zipkin - http://localhost:9411 - -To stop port forwarding run - -```shell -make portstop -``` - - -## Help - -To find the list of all the commands with their short descriptions run: - -```shell -make help -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - +# Dapr Cluster Setup + +An opinionated deployment of Dapr on Kubernetes, configured with: + +* Ingress with custom domain and TLS termination + * [NGINX](https://nginx.org/en/) for ingress controller and TLS to service mapping + * [letsencrypt](https://letsencrypt.org/) as certificate provider +* [KEDA](https://keda.sh/) for autoscaling +* Metrics Monitoring + * [Prometheus](https://prometheus.io/) for metrics aggregation + * [Grafana](https://grafana.com/) for metrics visualization with Dapr monitoring dashboards +* Log Management + * [Fluentd](https://www.fluentd.org/) for log collection and forwarding + * [Elasticsearch](https://www.elastic.co/) for log aggregation and query execution + * [Kibana](https://www.elastic.co/products/kibana) for full-text log query and visualization +* Distributed Tracing + * [Jaeger](https://www.jaegertracing.io/) for capturing traces, latency and dependency viewing + +> All demos in the [dapr-demo](../) repository are validated on this deployment + +## Prerequisites + +* 1.15+ Kubernates cluster. If needed, you can setup cluster on: + * [AKS](./aks/) + * [GKE](./gke/) + * AKS (coming) +* Tooling on the machine where you will be running this setup: + * [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) to do k8s stuff (`brew install kubectl`) + * [Helm 3](https://helm.sh/docs/intro/install/) to install Dapr and its dependencies (`brew install helm`) + * [certbot](https://certbot.eff.org/lets-encrypt/osx-other.html) to generate wildcard cert (`brew install certbot`) +* Domain name and access to the DNS service where you can manage that domain (required for letsencrypt challenge during cert generation and the `A` record creation to pont to the ingress gateway IP for custom domain support) + +## Setup + +The following parameters can be used to configure your deployment. Define these as environment variables to set or override the default value: + +```shell +DOMAIN # default: example.com +DAPR_HA # default: true +DAPR_LOG_AS_JSON # default: true +``` + +> Note, make sure the correct "target" cluster is set kubectl context (`kubectl config current-context`). You can lists all registered contexts using: `kubectl config get-contexts`, and if needed, set it using `kubectl config use-context demo`. + +## Usage + +Start by navigate to the [setup](./setup) directory + +> Run `make` by itself to see the active configuration + +To deploy and configure Dapr + +* `make dapr` to install Dapr, KEDA, and the entire observability stack +* `make config` to perform post-install configurations + +> Optionally you can use `make upgrade` to in place upgrade Dapr to specific version + +To configure external access + +* `make ip` (optional) to create static IP in the cluster resource group +* `make certs` to create TLS certs using letsencrypt +* `make ingress` to configures NGINX ingress, SSL termination, Dapr API auth +* `make dns` to configure your DNS service for custom domain support +* `make test` to test deployment + +To deploy in-cluster data services + +* `make redis` to install Redis into the cluster +* `make mongo` to install Mongo into the cluster +* `make kafka` to install Kafka into the cluster + +And few cluster operations helpers + +* `node-list` to print node resources and their usage +* `make ports` to forward observability dashboards ports +* `make pass` to print the Grafana password (username: admin) + +Then for each namespace you want to deploy Dapr apps to + +* `make namespace` to create/configure namespace with service secrets + +## Accessing observability dashboards + +To get access to the Kibana, Grafana, Zipkin dashboards run: + +```shell +make ports +``` + +This will forward the necessary ports so you can access the dashboards using: + +* kibana - http://localhost:5601 +* grafana - http://localhost:8888 +* zipkin - http://localhost:9411 + +To stop port forwarding run + +```shell +make portstop +``` + + +## Help + +To find the list of all the commands with their short descriptions run: + +```shell +make help +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + This software is released under the [MIT](../LICENSE) \ No newline at end of file diff --git a/setup/aks/Makefile b/setup/aks/Makefile index 43532d0..26437f6 100644 --- a/setup/aks/Makefile +++ b/setup/aks/Makefile @@ -1,66 +1,67 @@ -CLUSTER_NAME ?=demo -CLUSTER_VERSION ?=1.18.10 -NODE_COUNT ?=3 -NODE_TYPE ?=Standard_D4_v2 - -.PHONY: all -all: - @echo === ACTIVE CONFIGURATION === - @echo "CLUSTER_NAME: ${CLUSTER_NAME}" - @echo "CLUSTER_VERSION: ${CLUSTER_VERSION}" - @echo "NODE_COUNT: ${NODE_COUNT}" - @echo "NODE_TYPE: ${NODE_TYPE}" - @echo - @echo "Export these as environment variables to change their values" - @echo - - -.PHONY: cluster-list -cluster-list: ## List AKS clusters - az aks list -o table - -.PHONY: version-list -version-list: ## List Kubernetes versions supported in AKS - az aks get-versions -o json --query 'orchestrators[].orchestratorVersion' - -.PHONY: cluster -cluster: ## Create AKS cluster - az aks create \ - --name $(CLUSTER_NAME) \ - --node-count $(NODE_COUNT) \ - --node-vm-size $(NODE_TYPE) \ - --kubernetes-version $(CLUSTER_VERSION) - az aks get-credentials --name $(CLUSTER_NAME) - -.PHONY: cluster-down -cluster-down: cluster-list ## Delete previously created AKS cluster (make clusterdown CLUSTER_NAME=demo) - az aks delete --name $(CLUSTER_NAME) - -.PHONY: ip -ip: ## Create Static IP for the existing AKS cluster - $(eval CLUSTERRC=$(shell az aks show -n $(CLUSTER_NAME) -o tsv --query nodeResourceGroup)) - az network public-ip create \ - -g $(CLUSTERRC) \ - -n "${CLUSTER_NAME}IP" \ - --sku STANDARD - az network public-ip show \ - -g $(CLUSTERRC) \ - -n "${CLUSTER_NAME}IP" \ - -o tsv --query ipAddress - -.PHONY: node-pool -node-pool: ## Add new AKS node pool - $(eval NODE_SUFIX=$(shell date +"%d%m")) - az aks nodepool add \ - --cluster-name $(CLUSTER_NAME) \ - --name nodepool$(NODE_SUFIX) \ - --node-count $(NODE_COUNT) \ - --node-vm-size $(NODE_TYPE) \ - --mode System \ - --no-wait - - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +CLUSTER_NAME ?=mfusselldemo10 +CLUSTER_VERSION ?=1.18.10 +NODE_COUNT ?=3 +NODE_TYPE ?=Standard_D4_v2 + +.PHONY: all +all: + @echo === ACTIVE CONFIGURATION === + @echo "CLUSTER_NAME: ${CLUSTER_NAME}" + @echo "CLUSTER_VERSION: ${CLUSTER_VERSION}" + @echo "NODE_COUNT: ${NODE_COUNT}" + @echo "NODE_TYPE: ${NODE_TYPE}" + @echo + @echo "Export these as environment variables to change their values" + @echo + + +.PHONY: cluster-list +cluster-list: ## List AKS clusters + az aks list -o table + +.PHONY: version-list +version-list: ## List Kubernetes versions supported in AKS + az aks get-versions -o json --query 'orchestrators[].orchestratorVersion' + +.PHONY: cluster +cluster: ## Create AKS cluster + az aks create \ + --name $(CLUSTER_NAME) \ + --node-count $(NODE_COUNT) \ + --node-vm-size $(NODE_TYPE) \ + --kubernetes-version $(CLUSTER_VERSION) \ + -g mfussell + az aks get-credentials --name $(CLUSTER_NAME) + +.PHONY: cluster-down +cluster-down: cluster-list ## Delete previously created AKS cluster (make clusterdown CLUSTER_NAME=demo) + az aks delete --name $(CLUSTER_NAME) + +.PHONY: ip +ip: ## Create Static IP for the existing AKS cluster + $(eval CLUSTERRC=$(shell az aks show -n $(CLUSTER_NAME) -o tsv --query nodeResourceGroup)) + az network public-ip create \ + -g $(CLUSTERRC) \ + -n "${CLUSTER_NAME}IP" \ + --sku STANDARD + az network public-ip show \ + -g $(CLUSTERRC) \ + -n "${CLUSTER_NAME}IP" \ + -o tsv --query ipAddress + +.PHONY: node-pool +node-pool: ## Add new AKS node pool + $(eval NODE_SUFIX=$(shell date +"%d%m")) + az aks nodepool add \ + --cluster-name $(CLUSTER_NAME) \ + --name nodepool$(NODE_SUFIX) \ + --node-count $(NODE_COUNT) \ + --node-vm-size $(NODE_TYPE) \ + --mode System \ + --no-wait + + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/setup/aks/README.md b/setup/aks/README.md index 83dae1d..16cd003 100644 --- a/setup/aks/README.md +++ b/setup/aks/README.md @@ -1,64 +1,64 @@ -# AKS Cluster Setup - -The following parameters can be used to configure your deployment. Define these as environment variables to set or override the default value: - -```shell -CLUSTER_NAME # default: demo -CLUSTER_VERSION # default: 1.18.8 -NODE_COUNT # default: 3 -NODE_TYPE # default: Standard_D4_v2 -``` - -> Note, this assumes your default Azure resource group and location are already defined. If not, run - -```shell -az account set --subscription -az configure --defaults location= group= -``` - -## Usage - -Start by navigating to the [setup/aks](./setup/aks) directory - -> Run `make` by itself to see the active configuration - -* `make cluster` to create a cluster on AKS (make cluster CLUSTER_NAME=demo) -* `make ip` (optional) to create static IP in the cluster resource group -* `make node-pool` (optional) to add new AKS node pool -* `make node-list` to print node resource usage -* `make cluster-list` to list your AKS clusters -* `make version-list` to list Kubernetes versions supported on AKS - -Once cluster is created, you can follow [these instructions](../) to configure Dapr. - -## Cleanup - -To lists previously created clusters run - -```shell -make cluster-list -``` - -To delete any of the previously created clusters run - -> yes, there will be a prompt to confirm before deleting - -```shell -make cluster-down CLUSTER_NAME=name -``` - -## Help - -To find the list of all the commands with their short descriptions run: - -```shell -make help -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - +# AKS Cluster Setup + +The following parameters can be used to configure your deployment. Define these as environment variables to set or override the default value: + +```shell +CLUSTER_NAME # default: demo +CLUSTER_VERSION # default: 1.18.8 +NODE_COUNT # default: 3 +NODE_TYPE # default: Standard_D4_v2 +``` + +> Note, this assumes your default Azure resource group and location are already defined. If not, run + +```shell +az account set --subscription +az configure --defaults location= group= +``` + +## Usage + +Start by navigating to the [setup/aks](./setup/aks) directory + +> Run `make` by itself to see the active configuration + +* `make cluster` to create a cluster on AKS (make cluster CLUSTER_NAME=demo) +* `make ip` (optional) to create static IP in the cluster resource group +* `make node-pool` (optional) to add new AKS node pool +* `make node-list` to print node resource usage +* `make cluster-list` to list your AKS clusters +* `make version-list` to list Kubernetes versions supported on AKS + +Once cluster is created, you can follow [these instructions](../) to configure Dapr. + +## Cleanup + +To lists previously created clusters run + +```shell +make cluster-list +``` + +To delete any of the previously created clusters run + +> yes, there will be a prompt to confirm before deleting + +```shell +make cluster-down CLUSTER_NAME=name +``` + +## Help + +To find the list of all the commands with their short descriptions run: + +```shell +make help +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + This software is released under the [MIT](../../LICENSE) \ No newline at end of file diff --git a/setup/config/actor-dashboard.json b/setup/config/actor-dashboard.json index 1f186b7..f33f85f 100644 --- a/setup/config/actor-dashboard.json +++ b/setup/config/actor-dashboard.json @@ -1,875 +1,875 @@ -{ - "dashboard": { - "__inputs": [ - { - "name": "DS_DAPR", - "label": "Dapr", - "description": "", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "6.7.3" - }, - { - "type": "panel", - "id": "graph", - "name": "Graph", - "version": "" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - } - ], - "annotations": { - "list": [ - { - "$$hashKey": "object:487", - "builtIn": 1, - "datasource": "Dapr", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "limit": 100, - "name": "Annotations & Alerts", - "showIn": 0, - "type": "dashboard" - } - ] - }, - "description": "This is the dashboard for Dapr Actor", - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": null, - "iteration": 1591551276071, - "links": [], - "panels": [ - { - "collapsed": true, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 21, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows the turn-around latency when user app calls Dapr Actor API. For example, you can understand each statestore and service invocation performance.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 1 - }, - "hiddenSeries": false, - "id": 19, - "legend": { - "alignAsTable": false, - "avg": false, - "current": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.95, sum(rate(dapr_http_server_latency_bucket{path=~\"/v1.0/actors/$dapr_actor_type/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "[95p] {{method}} {{path}} ({{app_id}})", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.75, sum(rate(dapr_http_server_latency_bucket{path=~\"/v1.0/actors/$dapr_actor_type/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "[75p] {{method}} {{path}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor API Latency (App->Dapr) (95p, 75p)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1089", - "decimals": 2, - "format": "ms", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1090", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows completed request rate (rps) when User app calls Dapr Actor APIs.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 1 - }, - "hiddenSeries": false, - "id": 31, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum by (app_id, method, path) (rate(dapr_http_server_response_count{path=~\"/v1.0/actors/$dapr_actor_type/.*\", kubernetes_namespace=\"$namespace\"}[5m]))", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{method}} {{path}} ({{app_id}})", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor API request rate (App -> Dapr) (RPS)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:191", - "decimals": 2, - "format": "reqps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:192", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows the inbound request latency from Dapr to App. You can analyze App's API endpoint performance.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "hiddenSeries": false, - "id": 18, - "legend": { - "avg": false, - "current": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.95, sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "[95p] {{method}} /{{path}} ({{app_id}})", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.75, sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "[75p] {{method}} /{{path}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor Callback latency (Dapr -> App) (95p, 75p)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:334", - "decimals": 2, - "format": "ms", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:335", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows request rate (rps) when Dapr runtime calls User app HTTP endpoints.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "hiddenSeries": false, - "id": 32, - "interval": "", - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum by (app_id, method, path) (rate(dapr_http_client_completed_count{path=~\"actors/$dapr_actor_type/.*/method/.*\", kubernetes_namespace=\"$namespace\"}[5m]))", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{method}} {{path}} ({{app_id}})", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor Callback request rate (Dapr -> App) (RPS)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:411", - "decimals": 2, - "format": "reqps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:412", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Throughput/latency", - "type": "row" - }, - { - "collapsed": false, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 71, - "panels": [], - "title": "Timer & Reminder", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "Timer trigger status", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 2 - }, - "hiddenSeries": false, - "id": 72, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/timer/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (path)", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{path}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor timer trigger", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1552", - "decimals": 1, - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1553", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "Reminder trigger status", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 2 - }, - "hiddenSeries": false, - "id": 73, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/remind/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (path)", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{path}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor reminder trigger", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1552", - "decimals": 1, - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1553", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 69, - "panels": [], - "title": "Concurrency", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "It contains the number of pending actor calls that are waiting to acquire the per-actor lock that enforces turn-based concurrency.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 11 - }, - "hiddenSeries": false, - "id": 67, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_runtime_actor_pending_actor_calls{actor_type=~\"$dapr_actor_type\"}", - "interval": "", - "legendFormat": "{{actor_type}}.{{actor_id}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Pending locks Per Actor", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:977", - "format": "short", - "label": "Pending Locks", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:978", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "10s", - "schemaVersion": 22, - "style": "dark", - "tags": [ "autosetup" ], - "templating": { - "list": [ - { - "allValue": null, - "current": {}, - "datasource": "Dapr", - "definition": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", - "hide": 0, - "includeAll": false, - "index": -1, - "label": "NAMESPACE", - "multi": false, - "name": "namespace", - "options": [], - "query": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Dapr", - "definition": "label_values(dapr_runtime_actor_pending_actor_calls,actor_type)", - "hide": 0, - "includeAll": false, - "index": -1, - "label": "ACTOR TYPE", - "multi": true, - "name": "dapr_actor_type", - "options": [], - "query": "label_values(dapr_runtime_actor_pending_actor_calls,actor_type)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "browser", - "title": "Dapr Actor Dashboard", - "variables": { - "list": [] - }, - "version": 0 - }, - "overwrite": true +{ + "dashboard": { + "__inputs": [ + { + "name": "DS_DAPR", + "label": "Dapr", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "6.7.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:487", + "builtIn": 1, + "datasource": "Dapr", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "type": "dashboard" + } + ] + }, + "description": "This is the dashboard for Dapr Actor", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1591551276071, + "links": [], + "panels": [ + { + "collapsed": true, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 21, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows the turn-around latency when user app calls Dapr Actor API. For example, you can understand each statestore and service invocation performance.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 19, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(dapr_http_server_latency_bucket{path=~\"/v1.0/actors/$dapr_actor_type/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "[95p] {{method}} {{path}} ({{app_id}})", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.75, sum(rate(dapr_http_server_latency_bucket{path=~\"/v1.0/actors/$dapr_actor_type/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "[75p] {{method}} {{path}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor API Latency (App->Dapr) (95p, 75p)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1089", + "decimals": 2, + "format": "ms", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1090", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows completed request rate (rps) when User app calls Dapr Actor APIs.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "hiddenSeries": false, + "id": 31, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (app_id, method, path) (rate(dapr_http_server_response_count{path=~\"/v1.0/actors/$dapr_actor_type/.*\", kubernetes_namespace=\"$namespace\"}[5m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "{{method}} {{path}} ({{app_id}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor API request rate (App -> Dapr) (RPS)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:191", + "decimals": 2, + "format": "reqps", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:192", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows the inbound request latency from Dapr to App. You can analyze App's API endpoint performance.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "[95p] {{method}} /{{path}} ({{app_id}})", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.75, sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "[75p] {{method}} /{{path}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor Callback latency (Dapr -> App) (95p, 75p)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:334", + "decimals": 2, + "format": "ms", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:335", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows request rate (rps) when Dapr runtime calls User app HTTP endpoints.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 32, + "interval": "", + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (app_id, method, path) (rate(dapr_http_client_completed_count{path=~\"actors/$dapr_actor_type/.*/method/.*\", kubernetes_namespace=\"$namespace\"}[5m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "{{method}} {{path}} ({{app_id}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor Callback request rate (Dapr -> App) (RPS)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:411", + "decimals": 2, + "format": "reqps", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:412", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "Throughput/latency", + "type": "row" + }, + { + "collapsed": false, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 71, + "panels": [], + "title": "Timer & Reminder", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "Timer trigger status", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 2 + }, + "hiddenSeries": false, + "id": 72, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/timer/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (path)", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "{{path}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor timer trigger", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1552", + "decimals": 1, + "format": "none", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1553", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "Reminder trigger status", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 2 + }, + "hiddenSeries": false, + "id": 73, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(dapr_http_client_roundtrip_latency_bucket{path=~\"actors/$dapr_actor_type/.*/method/remind/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (path)", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "{{path}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor reminder trigger", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1552", + "decimals": 1, + "format": "none", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1553", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 69, + "panels": [], + "title": "Concurrency", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "It contains the number of pending actor calls that are waiting to acquire the per-actor lock that enforces turn-based concurrency.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 11 + }, + "hiddenSeries": false, + "id": 67, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_runtime_actor_pending_actor_calls{actor_type=~\"$dapr_actor_type\"}", + "interval": "", + "legendFormat": "{{actor_type}}.{{actor_id}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Pending locks Per Actor", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:977", + "format": "short", + "label": "Pending Locks", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:978", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "10s", + "schemaVersion": 22, + "style": "dark", + "tags": [ "autosetup" ], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "Dapr", + "definition": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", + "hide": 0, + "includeAll": false, + "index": -1, + "label": "NAMESPACE", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "Dapr", + "definition": "label_values(dapr_runtime_actor_pending_actor_calls,actor_type)", + "hide": 0, + "includeAll": false, + "index": -1, + "label": "ACTOR TYPE", + "multi": true, + "name": "dapr_actor_type", + "options": [], + "query": "label_values(dapr_runtime_actor_pending_actor_calls,actor_type)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "browser", + "title": "Dapr Actor Dashboard", + "variables": { + "list": [] + }, + "version": 0 + }, + "overwrite": true } \ No newline at end of file diff --git a/setup/config/fluentd-config.yaml b/setup/config/fluentd-config.yaml index ab9c7dc..cc6728b 100644 --- a/setup/config/fluentd-config.yaml +++ b/setup/config/fluentd-config.yaml @@ -1,105 +1,105 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: fluentd-config - namespace: kube-system -data: - fluent.conf: | - - @type null - - - - @type null - - - - @type null - - - - @type null - - - - @type tail - path /var/log/containers/*.log - pos_file fluentd-docker.pos - time_format %Y-%m-%dT%H:%M:%S - tag kubernetes.* - - @type multi_format - - format json - time_key time - time_type string - time_format "%Y-%m-%dT%H:%M:%S.%NZ" - keep_time_key false - - - format regexp - expression /^(? - - - - - @type kubernetes_metadata - @id filter_kube_metadata - - - - @type parser - - @type json - format json - time_key time - time_type string - time_format "%Y-%m-%dT%H:%M:%S.%NZ" - keep_time_key false - - key_name log - replace_invalid_sequence true - emit_invalid_record_to_error true - reserve_data true - - - - @type elasticsearch - @id out_es - @log_level info - include_tag_key true - host "#{ENV['FLUENT_ELASTICSEARCH_HOST']}" - port "#{ENV['FLUENT_ELASTICSEARCH_PORT']}" - path "#{ENV['FLUENT_ELASTICSEARCH_PATH']}" - scheme "#{ENV['FLUENT_ELASTICSEARCH_SCHEME'] || 'http'}" - ssl_verify "#{ENV['FLUENT_ELASTICSEARCH_SSL_VERIFY'] || 'true'}" - ssl_version "#{ENV['FLUENT_ELASTICSEARCH_SSL_VERSION'] || 'TLSv1_2'}" - user "#{ENV['FLUENT_ELASTICSEARCH_USER'] || use_default}" - password "#{ENV['FLUENT_ELASTICSEARCH_PASSWORD'] || use_default}" - reload_connections "#{ENV['FLUENT_ELASTICSEARCH_RELOAD_CONNECTIONS'] || 'false'}" - reconnect_on_error "#{ENV['FLUENT_ELASTICSEARCH_RECONNECT_ON_ERROR'] || 'true'}" - reload_on_failure "#{ENV['FLUENT_ELASTICSEARCH_RELOAD_ON_FAILURE'] || 'true'}" - log_es_400_reason "#{ENV['FLUENT_ELASTICSEARCH_LOG_ES_400_REASON'] || 'false'}" - logstash_prefix "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_PREFIX'] || 'dapr'}" - logstash_dateformat "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_DATEFORMAT'] || '%Y.%m.%d'}" - logstash_format "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_FORMAT'] || 'true'}" - index_name "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_INDEX_NAME'] || 'dapr'}" - type_name "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_TYPE_NAME'] || 'fluentd'}" - include_timestamp "#{ENV['FLUENT_ELASTICSEARCH_INCLUDE_TIMESTAMP'] || 'false'}" - template_name "#{ENV['FLUENT_ELASTICSEARCH_TEMPLATE_NAME'] || use_nil}" - template_file "#{ENV['FLUENT_ELASTICSEARCH_TEMPLATE_FILE'] || use_nil}" - template_overwrite "#{ENV['FLUENT_ELASTICSEARCH_TEMPLATE_OVERWRITE'] || use_default}" - sniffer_class_name "#{ENV['FLUENT_SNIFFER_CLASS_NAME'] || 'Fluent::Plugin::ElasticsearchSimpleSniffer'}" - request_timeout "#{ENV['FLUENT_ELASTICSEARCH_REQUEST_TIMEOUT'] || '5s'}" - - flush_thread_count "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_FLUSH_THREAD_COUNT'] || '8'}" - flush_interval "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_FLUSH_INTERVAL'] || '5s'}" - chunk_limit_size "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_CHUNK_LIMIT_SIZE'] || '2M'}" - queue_limit_length "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_QUEUE_LIMIT_LENGTH'] || '32'}" - retry_max_interval "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_RETRY_MAX_INTERVAL'] || '30'}" - retry_forever true - +apiVersion: v1 +kind: ConfigMap +metadata: + name: fluentd-config + namespace: kube-system +data: + fluent.conf: | + + @type null + + + + @type null + + + + @type null + + + + @type null + + + + @type tail + path /var/log/containers/*.log + pos_file fluentd-docker.pos + time_format %Y-%m-%dT%H:%M:%S + tag kubernetes.* + + @type multi_format + + format json + time_key time + time_type string + time_format "%Y-%m-%dT%H:%M:%S.%NZ" + keep_time_key false + + + format regexp + expression /^(? + + + + + @type kubernetes_metadata + @id filter_kube_metadata + + + + @type parser + + @type json + format json + time_key time + time_type string + time_format "%Y-%m-%dT%H:%M:%S.%NZ" + keep_time_key false + + key_name log + replace_invalid_sequence true + emit_invalid_record_to_error true + reserve_data true + + + + @type elasticsearch + @id out_es + @log_level info + include_tag_key true + host "#{ENV['FLUENT_ELASTICSEARCH_HOST']}" + port "#{ENV['FLUENT_ELASTICSEARCH_PORT']}" + path "#{ENV['FLUENT_ELASTICSEARCH_PATH']}" + scheme "#{ENV['FLUENT_ELASTICSEARCH_SCHEME'] || 'http'}" + ssl_verify "#{ENV['FLUENT_ELASTICSEARCH_SSL_VERIFY'] || 'true'}" + ssl_version "#{ENV['FLUENT_ELASTICSEARCH_SSL_VERSION'] || 'TLSv1_2'}" + user "#{ENV['FLUENT_ELASTICSEARCH_USER'] || use_default}" + password "#{ENV['FLUENT_ELASTICSEARCH_PASSWORD'] || use_default}" + reload_connections "#{ENV['FLUENT_ELASTICSEARCH_RELOAD_CONNECTIONS'] || 'false'}" + reconnect_on_error "#{ENV['FLUENT_ELASTICSEARCH_RECONNECT_ON_ERROR'] || 'true'}" + reload_on_failure "#{ENV['FLUENT_ELASTICSEARCH_RELOAD_ON_FAILURE'] || 'true'}" + log_es_400_reason "#{ENV['FLUENT_ELASTICSEARCH_LOG_ES_400_REASON'] || 'false'}" + logstash_prefix "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_PREFIX'] || 'dapr'}" + logstash_dateformat "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_DATEFORMAT'] || '%Y.%m.%d'}" + logstash_format "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_FORMAT'] || 'true'}" + index_name "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_INDEX_NAME'] || 'dapr'}" + type_name "#{ENV['FLUENT_ELASTICSEARCH_LOGSTASH_TYPE_NAME'] || 'fluentd'}" + include_timestamp "#{ENV['FLUENT_ELASTICSEARCH_INCLUDE_TIMESTAMP'] || 'false'}" + template_name "#{ENV['FLUENT_ELASTICSEARCH_TEMPLATE_NAME'] || use_nil}" + template_file "#{ENV['FLUENT_ELASTICSEARCH_TEMPLATE_FILE'] || use_nil}" + template_overwrite "#{ENV['FLUENT_ELASTICSEARCH_TEMPLATE_OVERWRITE'] || use_default}" + sniffer_class_name "#{ENV['FLUENT_SNIFFER_CLASS_NAME'] || 'Fluent::Plugin::ElasticsearchSimpleSniffer'}" + request_timeout "#{ENV['FLUENT_ELASTICSEARCH_REQUEST_TIMEOUT'] || '5s'}" + + flush_thread_count "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_FLUSH_THREAD_COUNT'] || '8'}" + flush_interval "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_FLUSH_INTERVAL'] || '5s'}" + chunk_limit_size "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_CHUNK_LIMIT_SIZE'] || '2M'}" + queue_limit_length "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_QUEUE_LIMIT_LENGTH'] || '32'}" + retry_max_interval "#{ENV['FLUENT_ELASTICSEARCH_BUFFER_RETRY_MAX_INTERVAL'] || '30'}" + retry_forever true + \ No newline at end of file diff --git a/setup/config/fluentd.yaml b/setup/config/fluentd.yaml index b8e8c18..c77381f 100644 --- a/setup/config/fluentd.yaml +++ b/setup/config/fluentd.yaml @@ -1,98 +1,98 @@ ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: fluentd - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRole -metadata: - name: fluentd - namespace: kube-system -rules: -- apiGroups: - - "" - resources: - - pods - - namespaces - verbs: - - get - - list - - watch ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: fluentd - namespace: default -roleRef: - kind: ClusterRole - name: fluentd - apiGroup: rbac.authorization.k8s.io -subjects: -- kind: ServiceAccount - name: fluentd - namespace: kube-system ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: fluentd - namespace: kube-system - labels: - k8s-app: fluentd-logging - version: v1 -spec: - selector: - matchLabels: - k8s-app: fluentd-logging - version: v1 - template: - metadata: - labels: - k8s-app: fluentd-logging - version: v1 - spec: - serviceAccount: fluentd - serviceAccountName: fluentd - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule - containers: - - name: fluentd - image: fluent/fluentd-kubernetes-daemonset:v1.9.2-debian-elasticsearch7-1.0 - env: - - name: FLUENT_ELASTICSEARCH_HOST - value: "elasticsearch-master.dapr-monitoring" - - name: FLUENT_ELASTICSEARCH_PORT - value: "9200" - - name: FLUENT_ELASTICSEARCH_SCHEME - value: "http" - - name: FLUENT_UID - value: "0" - resources: - limits: - memory: 200Mi - requests: - cpu: 100m - memory: 200Mi - volumeMounts: - - name: varlog - mountPath: /var/log - - name: varlibdockercontainers - mountPath: /var/lib/docker/containers - readOnly: true - - name: fluentd-config - mountPath: /fluentd/etc - terminationGracePeriodSeconds: 30 - volumes: - - name: varlog - hostPath: - path: /var/log - - name: varlibdockercontainers - hostPath: - path: /var/lib/docker/containers - - name: fluentd-config - configMap: +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: fluentd + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: fluentd + namespace: kube-system +rules: +- apiGroups: + - "" + resources: + - pods + - namespaces + verbs: + - get + - list + - watch +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: fluentd + namespace: default +roleRef: + kind: ClusterRole + name: fluentd + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: fluentd + namespace: kube-system +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: fluentd + namespace: kube-system + labels: + k8s-app: fluentd-logging + version: v1 +spec: + selector: + matchLabels: + k8s-app: fluentd-logging + version: v1 + template: + metadata: + labels: + k8s-app: fluentd-logging + version: v1 + spec: + serviceAccount: fluentd + serviceAccountName: fluentd + tolerations: + - key: node-role.kubernetes.io/master + effect: NoSchedule + containers: + - name: fluentd + image: fluent/fluentd-kubernetes-daemonset:v1.9.2-debian-elasticsearch7-1.0 + env: + - name: FLUENT_ELASTICSEARCH_HOST + value: "elasticsearch-master.dapr-monitoring" + - name: FLUENT_ELASTICSEARCH_PORT + value: "9200" + - name: FLUENT_ELASTICSEARCH_SCHEME + value: "http" + - name: FLUENT_UID + value: "0" + resources: + limits: + memory: 200Mi + requests: + cpu: 100m + memory: 200Mi + volumeMounts: + - name: varlog + mountPath: /var/log + - name: varlibdockercontainers + mountPath: /var/lib/docker/containers + readOnly: true + - name: fluentd-config + mountPath: /fluentd/etc + terminationGracePeriodSeconds: 30 + volumes: + - name: varlog + hostPath: + path: /var/log + - name: varlibdockercontainers + hostPath: + path: /var/lib/docker/containers + - name: fluentd-config + configMap: name: fluentd-config \ No newline at end of file diff --git a/setup/config/ingress-annotations.yaml b/setup/config/ingress-annotations.yaml index c81e17f..5bb0d52 100644 --- a/setup/config/ingress-annotations.yaml +++ b/setup/config/ingress-annotations.yaml @@ -1,9 +1,9 @@ -controller: - podAnnotations: - dapr.io/enabled: "true" - dapr.io/app-id: "nginx-ingress" - dapr.io/app-protocol: "http" - dapr.io/app-port: "80" - dapr.io/api-token-secret: "dapr-api-token" - dapr.io/config: "ingress-config" +controller: + podAnnotations: + dapr.io/enabled: "true" + dapr.io/app-id: "nginx-ingress" + dapr.io/app-protocol: "http" + dapr.io/app-port: "80" + dapr.io/api-token-secret: "dapr-api-token" + dapr.io/config: "ingress-config" dapr.io/log-as-json: "true" \ No newline at end of file diff --git a/setup/config/ingress-config.yaml b/setup/config/ingress-config.yaml index 2e5f302..d0e09e6 100644 --- a/setup/config/ingress-config.yaml +++ b/setup/config/ingress-config.yaml @@ -1,21 +1,21 @@ ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: ingress-config -spec: - tracing: - samplingRate: "1" - secrets: - scopes: - - storeName: kubernetes - defaultAccess: deny - allowedSecrets: ["dapr-api-token"] ---- -apiVersion: dapr.io/v1alpha1 -kind: Configuration -metadata: - name: tracing -spec: - tracing: +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: ingress-config +spec: + tracing: + samplingRate: "1" + secrets: + scopes: + - storeName: kubernetes + defaultAccess: deny + allowedSecrets: ["dapr-api-token"] +--- +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: tracing +spec: + tracing: samplingRate: "1" \ No newline at end of file diff --git a/setup/config/ingress-template.yaml b/setup/config/ingress-template.yaml index dd33d21..8d78bc3 100644 --- a/setup/config/ingress-template.yaml +++ b/setup/config/ingress-template.yaml @@ -1,22 +1,22 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: ingress-rules - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/rewrite-target: / -spec: - tls: - - hosts: - - '*.DOMAINNAME' - - DOMAINNAME - secretName: tls-secret - rules: - - host: api.DOMAINNAME - http: - paths: - - path: / - backend: - serviceName: nginx-ingress-dapr - servicePort: 80 +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: ingress-rules + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + tls: + - hosts: + - '*.DOMAINNAME' + - DOMAINNAME + secretName: tls-secret + rules: + - host: api.DOMAINNAME + http: + paths: + - path: / + backend: + serviceName: nginx-ingress-dapr + servicePort: 80 \ No newline at end of file diff --git a/setup/config/namespace-template.yml b/setup/config/namespace-template.yml index 95d275c..c70cb6d 100644 --- a/setup/config/namespace-template.yml +++ b/setup/config/namespace-template.yml @@ -1,40 +1,40 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: NSNAME ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: secret-reader - namespace: NSNAME -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: dapr-secret-reader - namespace: NSNAME -subjects: -- kind: ServiceAccount - name: default -roleRef: - kind: Role - name: secret-reader - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: zipkin - namespace: NSNAME -spec: - type: exporters.zipkin - metadata: - - name: enabled - value: "true" - - name: exporterAddress +apiVersion: v1 +kind: Namespace +metadata: + name: NSNAME +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secret-reader + namespace: NSNAME +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: dapr-secret-reader + namespace: NSNAME +subjects: +- kind: ServiceAccount + name: default +roleRef: + kind: Role + name: secret-reader + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: zipkin + namespace: NSNAME +spec: + type: exporters.zipkin + metadata: + - name: enabled + value: "true" + - name: exporterAddress value: "http://zipkin.dapr-monitoring.svc.cluster.local:9411/api/v2/spans" \ No newline at end of file diff --git a/setup/config/sidecar-dashboard.json b/setup/config/sidecar-dashboard.json index ff08488..74b6295 100644 --- a/setup/config/sidecar-dashboard.json +++ b/setup/config/sidecar-dashboard.json @@ -1,2345 +1,2345 @@ -{ - "dashboard": { - "__inputs": [ - { - "name": "DS_DAPR", - "label": "Dapr", - "description": "", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__requires": [ - { - "type": "panel", - "id": "bargauge", - "name": "Bar Gauge", - "version": "" - }, - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "6.7.3" - }, - { - "type": "panel", - "id": "graph", - "name": "Graph", - "version": "" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "stat", - "name": "Stat", - "version": "" - } - ], - "annotations": { - "list": [ - { - "$$hashKey": "object:356", - "builtIn": 1, - "datasource": "Dapr", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "limit": 100, - "name": "Annotations & Alerts", - "showIn": 0, - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": null, - "iteration": 1591551058714, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 16, - "panels": [], - "title": "Health monitor", - "type": "row" - }, - { - "cacheTimeout": null, - "datasource": "Dapr", - "description": "This shows the uptime of dapr runtime sidecar. If the sidecar is running for less than 10 minutes, the color will turn into RED.", - "gridPos": { - "h": 4, - "w": 12, - "x": 0, - "y": 1 - }, - "id": 70, - "links": [], - "options": { - "colorMode": "value", - "fieldOptions": { - "calcs": [ - "last" - ], - "defaults": { - "decimals": 2, - "mappings": [ - { - "id": 0, - "op": "=", - "text": "N/A", - "type": 1, - "value": "null" - } - ], - "nullValueMode": "connected", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "green", - "value": 600 - } - ] - }, - "title": "", - "unit": "s" - }, - "overrides": [], - "values": false - }, - "graphMode": "area", - "justifyMode": "auto", - "orientation": "vertical" - }, - "pluginVersion": "6.7.3", - "targets": [ - { - "expr": "time() - max(process_start_time_seconds{kubernetes_name=~\"($dapr_app_id)-dapr\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", - "legendFormat": "{{kubernetes_name}}", - "refId": "A" - } - ], - "timeFrom": "5m", - "timeShift": null, - "title": "Sidecar uptime", - "type": "stat" - }, - { - "collapsed": false, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 5 - }, - "id": 72, - "panels": [], - "title": "Resource usage", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This shows total amount of Kernel and user CPU usage for Daprd.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 6 - }, - "hiddenSeries": false, - "id": 9, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{pod_name=~\"($dapr_app_id).*\", namespace=\"$namespace\"}[5m])) by (pod_name)", - "legendFormat": "{{pod_name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Total CPU usage (Kernel and User)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "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": "Dapr", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 6 - }, - "hiddenSeries": false, - "id": 11, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(go_goroutines{kubernetes_name=~\"($dapr_app_id)-dapr.*\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", - "legendFormat": "{{kubernetes_name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Number of GO routines", - "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 - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "The amount of memory that belongs specifically to that process in bytes. This excludes swapped out memory pages.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 6, - "x": 12, - "y": 6 - }, - "hiddenSeries": false, - "id": 10, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(process_resident_memory_bytes{kubernetes_name=~\"($dapr_app_id)-dapr\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", - "legendFormat": "{{kubernetes_name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory usage in bytes", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "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": "Dapr", - "description": "The amount of address space that a process is managing. This includes all types of memory, both in RAM and swapped out.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 6, - "x": 18, - "y": 6 - }, - "hiddenSeries": false, - "id": 12, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(process_virtual_memory_bytes{kubernetes_name=~\"($dapr_app_id)-dapr\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", - "legendFormat": "{{kubernetes_name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Managed virtual memory in bytes", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 1, - "format": "decbytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": true, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 14 - }, - "id": 21, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows the turn-around latency when user app calls Dapr API. For example, you can understand each statestore and service invocation performance.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 7 - }, - "hiddenSeries": false, - "id": 19, - "legend": { - "alignAsTable": false, - "avg": false, - "current": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.95, sum(rate(dapr_http_server_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "legendFormat": "[95p] {{method}} {{path}} ({{app_id}})", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.75, sum(rate(dapr_http_server_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "legendFormat": "[75p] {{method}} {{path}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outbound request latency (App->Dapr) (95p, 75p)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 2, - "format": "ms", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows completed request rate (rps) when User app calls Dapr.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 7 - }, - "hiddenSeries": false, - "id": 31, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum by (app_id, method, path) (rate(dapr_http_server_response_count{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m]))", - "format": "time_series", - "instant": false, - "legendFormat": "{{method}} {{path}} ({{app_id}})", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outbound request rate (App -> Dapr) (RPS)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 2, - "format": "reqps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows the inbound request latency from Dapr to App. You can analyze App's API endpoint performance.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 15 - }, - "hiddenSeries": false, - "id": 18, - "legend": { - "avg": false, - "current": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.95, sum(rate(dapr_http_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "legendFormat": "[95p] {{method}} /{{path}} ({{app_id}})", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.75, sum(rate(dapr_http_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", - "format": "time_series", - "instant": false, - "legendFormat": "[75p] {{method}} /{{path}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Inbound request latency (Dapr -> App) (95p, 75p)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 2, - "format": "ms", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows request rate (rps) when Dapr runtime calls User app HTTP endpoints.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 15 - }, - "hiddenSeries": false, - "id": 32, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "sideWidth": 150, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum by (app_id, method, path) (rate(dapr_http_client_completed_count{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m]))", - "format": "time_series", - "instant": false, - "legendFormat": "{{method}} {{path}} ({{app_id}})", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Inbound request rate (Dapr -> App) (RPS)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 2, - "format": "reqps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Throughput/Latency - HTTP", - "type": "row" - }, - { - "collapsed": true, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 15 - }, - "id": 34, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This charts shows the turn-around latency when user app calls Dapr API. For example, you can understand each statestore and service invocation performance.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "hiddenSeries": false, - "id": 36, - "legend": { - "avg": false, - "current": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.95, sum(rate(dapr_grpc_io_server_server_latency_bucket{app_id=~\"$dapr_app_id\", grpc_server_method=~\"dapr.Dapr/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_server_method))", - "legendFormat": "[95p] - {{grpc_server_method}} ({{app_id}})", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.75, sum(rate(dapr_grpc_io_server_server_latency_bucket{app_id=~\"$dapr_app_id\", grpc_server_method=~\"dapr.Dapr/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_server_method))", - "legendFormat": "[75p] {{grpc_server_method}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outbound request latency (App->Dapr) (95p, 75p)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "ms", - "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": "Dapr", - "description": "This charts shows completed request rate (rps) when User app calls Dapr.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "hiddenSeries": false, - "id": 38, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum by (app_id, grpc_server_method, grpc_server_status) (rate(dapr_grpc_io_server_completed_rpcs{app_id=~\"$dapr_app_id\", grpc_server_method=~\"dapr.Dapr/.*\"}[5m]))", - "legendFormat": "{{grpc_server_status}} {{grpc_server_method}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Outbound request rate (App -> Dapr) (RPS)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "reqps", - "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": "Dapr", - "description": "This charts shows the inbound request latency from Dapr to App. You can analyze App's API endpoint performance.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 16 - }, - "hiddenSeries": false, - "id": 40, - "legend": { - "avg": false, - "current": true, - "max": false, - "min": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.95, sum(rate(dapr_grpc_io_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", grpc_client_method=~\"daprclient.DaprClient/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_client_method))", - "legendFormat": "[95p] {{grpc_client_method}} ({{app_id}})", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.75, sum(rate(dapr_grpc_io_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", grpc_client_method=~\"daprclient.DaprClient/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_client_method))", - "legendFormat": "[75p] {{grpc_client_method}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Inbound request latency (Dapr -> App) (95p, 75p)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "ms", - "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": "Dapr", - "description": "This charts shows request rate (rps) when Dapr runtime calls User app gRPC endpoints.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 16 - }, - "hiddenSeries": false, - "id": 42, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum by (app_id, grpc_client_method, grpc_client_status) (rate(dapr_grpc_io_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", grpc_client_method=~\"daprclient.DaprClient/.*\", kubernetes_namespace=\"$namespace\"}[5m]))", - "legendFormat": "{{grpc_client_status}} {{grpc_client_method}} ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Inbound request rate (Dapr -> App) (RPS)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "reqps", - "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 - } - } - ], - "title": "Throughput/Latency - gRPC", - "type": "row" - }, - { - "collapsed": true, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 16 - }, - "id": 52, - "panels": [ - { - "datasource": "Dapr", - "description": "This shows the number of configured components. For example, it will shows \"2\" if you configure redis statestore and redis pubsub components. ", - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 9 - }, - "id": 56, - "options": { - "colorMode": "value", - "fieldOptions": { - "calcs": [ - "last" - ], - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [], - "values": false - }, - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal" - }, - "pluginVersion": "6.6.2", - "targets": [ - { - "expr": "sum(dapr_runtime_component_loaded{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id)", - "legendFormat": "{{app_id}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Loaded component configurations", - "type": "stat" - }, - { - "cacheTimeout": null, - "datasource": "Dapr", - "description": "This shows the number of initialized components. This number must be the same as loaded components. Otherwise, Daprd fails to initialize some of components.", - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 9 - }, - "id": 54, - "links": [], - "options": { - "colorMode": "value", - "fieldOptions": { - "calcs": [ - "last" - ], - "defaults": { - "mappings": [ - { - "id": 0, - "op": "=", - "text": "N/A", - "type": 1, - "value": "null" - } - ], - "nullValueMode": "connected", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [], - "values": false - }, - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal" - }, - "pluginVersion": "6.6.2", - "targets": [ - { - "expr": "sum(dapr_runtime_component_init_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id)", - "legendFormat": "{{app_id}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Initialized components", - "type": "stat" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This chart shows the number component initialization failures with the reasons.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "hiddenSeries": false, - "id": 58, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(dapr_runtime_component_init_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, reason)", - "legendFormat": "{{app_id}} - {{reason}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Component failures", - "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": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Components", - "type": "row" - }, - { - "collapsed": true, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 17 - }, - "id": 44, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This shows when Daprd reports host status to placement service. Non-Actor service reports host status in the beginning. Actor service reports host status to placement periodically. If there is a failure, virtual actors will not be distributed properly.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 9, - "x": 0, - "y": 18 - }, - "hiddenSeries": false, - "id": 46, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": true, - "targets": [ - { - "expr": "rate(dapr_runtime_actor_status_report_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", - "legendFormat": "OK ({{app_id}})", - "refId": "A" - }, - { - "expr": "rate(dapr_runtime_actor_status_report_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", - "legendFormat": "Error ({{app_id}})", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Status report status", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "datasource": "Dapr", - "description": "This shows the failures when Daprd reports host status to placement.", - "gridPos": { - "h": 7, - "w": 5, - "x": 9, - "y": 18 - }, - "id": 59, - "links": [], - "options": { - "displayMode": "basic", - "fieldOptions": { - "calcs": [ - "max" - ], - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 10 - } - ] - } - }, - "overrides": [], - "values": false - }, - "orientation": "horizontal", - "showUnfilled": true - }, - "pluginVersion": "6.7.3", - "targets": [ - { - "expr": "sum(dapr_runtime_actor_status_report_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, operation)", - "legendFormat": "{{operation}} ({{app_id}})", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Report status error reasons", - "type": "bargauge" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This shows the placement table update status based on the response from placement service. Daprd maintains the actor placement table in its memory. This table is periodically updated.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 10, - "x": 14, - "y": 18 - }, - "hiddenSeries": false, - "id": 61, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 1, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(dapr_runtime_actor_table_operation_recv_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, operation)", - "format": "time_series", - "intervalFactor": 3, - "legendFormat": "{{operation}} ({{app_id}})", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Placement table operation", - "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 - } - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This chart shows how many actors are deactivated.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 9, - "x": 0, - "y": 25 - }, - "hiddenSeries": false, - "id": 65, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 1, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_runtime_actor_deactivated_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}", - "legendFormat": "OK {{actor_type}}", - "refId": "A" - }, - { - "expr": "dapr_runtime_actor_deactivated_failed_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}", - "legendFormat": "Error {{actor_type}}", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor Deactivation", - "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 - } - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This shows when actor is rebalanced. This operation can make the activated actors deactivated and move actor to the other hosts.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 7, - "x": 9, - "y": 25 - }, - "hiddenSeries": false, - "id": 63, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 1, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_runtime_actor_rebalanced_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}", - "intervalFactor": 1, - "legendFormat": "{{app_id}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Actor table rebalancing", - "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 - } - } - ], - "title": "Actor", - "type": "row" - }, - { - "collapsed": true, - "datasource": "Dapr", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 18 - }, - "id": 50, - "panels": [ - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This chart shows when mTLS is initialized, which means root cert is loaded and workload cert is issued from sentry, when runtime starts.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 7, - "x": 0, - "y": 11 - }, - "hiddenSeries": false, - "id": 48, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "rate(dapr_runtime_mtls_init_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", - "intervalFactor": 2, - "legendFormat": "OK {{app_id}}", - "refId": "A" - }, - { - "expr": "rate(dapr_runtime_mtls_init_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", - "intervalFactor": 2, - "legendFormat": "Error {{app_id}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "mTLS initialization status", - "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": false - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "datasource": "Dapr", - "gridPos": { - "h": 8, - "w": 5, - "x": 7, - "y": 11 - }, - "id": 67, - "links": [], - "options": { - "displayMode": "basic", - "fieldOptions": { - "calcs": [ - "mean" - ], - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 10 - } - ] - } - }, - "overrides": [], - "values": false - }, - "orientation": "horizontal", - "showUnfilled": true - }, - "pluginVersion": "6.6.2", - "targets": [ - { - "expr": "sum(dapr_runtime_mtls_init_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, reason)", - "legendFormat": "{{reason}} ({{app_id}})", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "mTLS Initialization Errors", - "type": "bargauge" - }, - { - "aliasColors": {}, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This chart shows when workload certificate is rotated.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 7, - "x": 12, - "y": 11 - }, - "hiddenSeries": false, - "id": 66, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": false, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "rate(dapr_runtime_mtls_workload_cert_rotated_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", - "format": "time_series", - "instant": false, - "intervalFactor": 2, - "legendFormat": "OK {{app_id}}", - "refId": "A" - }, - { - "expr": "rate(dapr_runtime_mtls_workload_cert_rotated_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", - "intervalFactor": 2, - "legendFormat": "Error {{app_id}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Workload certificate rotation status", - "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": false - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "datasource": "Dapr", - "gridPos": { - "h": 8, - "w": 5, - "x": 19, - "y": 11 - }, - "id": 68, - "links": [], - "options": { - "displayMode": "basic", - "fieldOptions": { - "calcs": [ - "mean" - ], - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 10 - } - ] - } - }, - "overrides": [], - "values": false - }, - "orientation": "horizontal", - "showUnfilled": true - }, - "pluginVersion": "6.6.2", - "targets": [ - { - "expr": "sum(dapr_runtime_mtls_workload_cert_rotated_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, reason)", - "legendFormat": "{{reason}} ({{app_id}})", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Cert rotation error reasons", - "type": "bargauge" - } - ], - "title": "Security", - "type": "row" - } - ], - "refresh": "10s", - "schemaVersion": 22, - "style": "dark", - "tags": [ "autosetup" ], - "templating": { - "list": [ - { - "allValue": null, - "current": {}, - "datasource": "Dapr", - "definition": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", - "hide": 0, - "includeAll": false, - "index": -1, - "label": "NAMESPACE", - "multi": false, - "name": "namespace", - "options": [], - "query": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "Dapr", - "definition": "label_values(dapr_runtime_component_loaded,app_id)", - "hide": 0, - "includeAll": false, - "index": -1, - "label": "APPID", - "multi": true, - "name": "dapr_app_id", - "options": [], - "query": "label_values(dapr_runtime_component_loaded,app_id)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "browser", - "title": "Dapr Sidecar Dashboard", - "variables": { - "list": [] - }, - "version": 0 - }, - "overwrite": true +{ + "dashboard": { + "__inputs": [ + { + "name": "DS_DAPR", + "label": "Dapr", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "panel", + "id": "bargauge", + "name": "Bar Gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "6.7.3" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + } + ], + "annotations": { + "list": [ + { + "$$hashKey": "object:356", + "builtIn": 1, + "datasource": "Dapr", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1591551058714, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 16, + "panels": [], + "title": "Health monitor", + "type": "row" + }, + { + "cacheTimeout": null, + "datasource": "Dapr", + "description": "This shows the uptime of dapr runtime sidecar. If the sidecar is running for less than 10 minutes, the color will turn into RED.", + "gridPos": { + "h": 4, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 70, + "links": [], + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "last" + ], + "defaults": { + "decimals": 2, + "mappings": [ + { + "id": 0, + "op": "=", + "text": "N/A", + "type": 1, + "value": "null" + } + ], + "nullValueMode": "connected", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 600 + } + ] + }, + "title": "", + "unit": "s" + }, + "overrides": [], + "values": false + }, + "graphMode": "area", + "justifyMode": "auto", + "orientation": "vertical" + }, + "pluginVersion": "6.7.3", + "targets": [ + { + "expr": "time() - max(process_start_time_seconds{kubernetes_name=~\"($dapr_app_id)-dapr\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", + "legendFormat": "{{kubernetes_name}}", + "refId": "A" + } + ], + "timeFrom": "5m", + "timeShift": null, + "title": "Sidecar uptime", + "type": "stat" + }, + { + "collapsed": false, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 72, + "panels": [], + "title": "Resource usage", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This shows total amount of Kernel and user CPU usage for Daprd.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 6 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{pod_name=~\"($dapr_app_id).*\", namespace=\"$namespace\"}[5m])) by (pod_name)", + "legendFormat": "{{pod_name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total CPU usage (Kernel and User)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "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": "Dapr", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 6 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(go_goroutines{kubernetes_name=~\"($dapr_app_id)-dapr.*\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", + "legendFormat": "{{kubernetes_name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Number of GO routines", + "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 + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "The amount of memory that belongs specifically to that process in bytes. This excludes swapped out memory pages.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 6 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(process_resident_memory_bytes{kubernetes_name=~\"($dapr_app_id)-dapr\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", + "legendFormat": "{{kubernetes_name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory usage in bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "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": "Dapr", + "description": "The amount of address space that a process is managing. This includes all types of memory, both in RAM and swapped out.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 6 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(process_virtual_memory_bytes{kubernetes_name=~\"($dapr_app_id)-dapr\", kubernetes_namespace=\"$namespace\"}) by (kubernetes_name)", + "legendFormat": "{{kubernetes_name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Managed virtual memory in bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "decbytes", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": true, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 21, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows the turn-around latency when user app calls Dapr API. For example, you can understand each statestore and service invocation performance.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 7 + }, + "hiddenSeries": false, + "id": 19, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(dapr_http_server_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "legendFormat": "[95p] {{method}} {{path}} ({{app_id}})", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.75, sum(rate(dapr_http_server_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "legendFormat": "[75p] {{method}} {{path}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Outbound request latency (App->Dapr) (95p, 75p)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "ms", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows completed request rate (rps) when User app calls Dapr.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 7 + }, + "hiddenSeries": false, + "id": 31, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (app_id, method, path) (rate(dapr_http_server_response_count{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m]))", + "format": "time_series", + "instant": false, + "legendFormat": "{{method}} {{path}} ({{app_id}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Outbound request rate (App -> Dapr) (RPS)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows the inbound request latency from Dapr to App. You can analyze App's API endpoint performance.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 15 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(dapr_http_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "legendFormat": "[95p] {{method}} /{{path}} ({{app_id}})", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.75, sum(rate(dapr_http_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, method, path))", + "format": "time_series", + "instant": false, + "legendFormat": "[75p] {{method}} /{{path}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Inbound request latency (Dapr -> App) (95p, 75p)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "ms", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows request rate (rps) when Dapr runtime calls User app HTTP endpoints.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 15 + }, + "hiddenSeries": false, + "id": 32, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": 150, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (app_id, method, path) (rate(dapr_http_client_completed_count{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m]))", + "format": "time_series", + "instant": false, + "legendFormat": "{{method}} {{path}} ({{app_id}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Inbound request rate (Dapr -> App) (RPS)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "Throughput/Latency - HTTP", + "type": "row" + }, + { + "collapsed": true, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 15 + }, + "id": 34, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This charts shows the turn-around latency when user app calls Dapr API. For example, you can understand each statestore and service invocation performance.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 36, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(dapr_grpc_io_server_server_latency_bucket{app_id=~\"$dapr_app_id\", grpc_server_method=~\"dapr.Dapr/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_server_method))", + "legendFormat": "[95p] - {{grpc_server_method}} ({{app_id}})", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.75, sum(rate(dapr_grpc_io_server_server_latency_bucket{app_id=~\"$dapr_app_id\", grpc_server_method=~\"dapr.Dapr/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_server_method))", + "legendFormat": "[75p] {{grpc_server_method}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Outbound request latency (App->Dapr) (95p, 75p)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "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": "Dapr", + "description": "This charts shows completed request rate (rps) when User app calls Dapr.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 38, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (app_id, grpc_server_method, grpc_server_status) (rate(dapr_grpc_io_server_completed_rpcs{app_id=~\"$dapr_app_id\", grpc_server_method=~\"dapr.Dapr/.*\"}[5m]))", + "legendFormat": "{{grpc_server_status}} {{grpc_server_method}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Outbound request rate (App -> Dapr) (RPS)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "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": "Dapr", + "description": "This charts shows the inbound request latency from Dapr to App. You can analyze App's API endpoint performance.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "hiddenSeries": false, + "id": 40, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(dapr_grpc_io_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", grpc_client_method=~\"daprclient.DaprClient/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_client_method))", + "legendFormat": "[95p] {{grpc_client_method}} ({{app_id}})", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.75, sum(rate(dapr_grpc_io_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", grpc_client_method=~\"daprclient.DaprClient/.*\", kubernetes_namespace=\"$namespace\"}[5m])) by (le, app_id, grpc_client_method))", + "legendFormat": "[75p] {{grpc_client_method}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Inbound request latency (Dapr -> App) (95p, 75p)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "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": "Dapr", + "description": "This charts shows request rate (rps) when Dapr runtime calls User app gRPC endpoints.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "hiddenSeries": false, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (app_id, grpc_client_method, grpc_client_status) (rate(dapr_grpc_io_client_roundtrip_latency_bucket{app_id=~\"$dapr_app_id\", grpc_client_method=~\"daprclient.DaprClient/.*\", kubernetes_namespace=\"$namespace\"}[5m]))", + "legendFormat": "{{grpc_client_status}} {{grpc_client_method}} ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Inbound request rate (Dapr -> App) (RPS)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "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 + } + } + ], + "title": "Throughput/Latency - gRPC", + "type": "row" + }, + { + "collapsed": true, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 52, + "panels": [ + { + "datasource": "Dapr", + "description": "This shows the number of configured components. For example, it will shows \"2\" if you configure redis statestore and redis pubsub components. ", + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 9 + }, + "id": 56, + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "last" + ], + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [], + "values": false + }, + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal" + }, + "pluginVersion": "6.6.2", + "targets": [ + { + "expr": "sum(dapr_runtime_component_loaded{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id)", + "legendFormat": "{{app_id}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Loaded component configurations", + "type": "stat" + }, + { + "cacheTimeout": null, + "datasource": "Dapr", + "description": "This shows the number of initialized components. This number must be the same as loaded components. Otherwise, Daprd fails to initialize some of components.", + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 9 + }, + "id": 54, + "links": [], + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "last" + ], + "defaults": { + "mappings": [ + { + "id": 0, + "op": "=", + "text": "N/A", + "type": 1, + "value": "null" + } + ], + "nullValueMode": "connected", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [], + "values": false + }, + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal" + }, + "pluginVersion": "6.6.2", + "targets": [ + { + "expr": "sum(dapr_runtime_component_init_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id)", + "legendFormat": "{{app_id}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Initialized components", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This chart shows the number component initialization failures with the reasons.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 58, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(dapr_runtime_component_init_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, reason)", + "legendFormat": "{{app_id}} - {{reason}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Component failures", + "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": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "Components", + "type": "row" + }, + { + "collapsed": true, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 44, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This shows when Daprd reports host status to placement service. Non-Actor service reports host status in the beginning. Actor service reports host status to placement periodically. If there is a failure, virtual actors will not be distributed properly.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 46, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": true, + "targets": [ + { + "expr": "rate(dapr_runtime_actor_status_report_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", + "legendFormat": "OK ({{app_id}})", + "refId": "A" + }, + { + "expr": "rate(dapr_runtime_actor_status_report_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", + "legendFormat": "Error ({{app_id}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Status report status", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cacheTimeout": null, + "datasource": "Dapr", + "description": "This shows the failures when Daprd reports host status to placement.", + "gridPos": { + "h": 7, + "w": 5, + "x": 9, + "y": 18 + }, + "id": 59, + "links": [], + "options": { + "displayMode": "basic", + "fieldOptions": { + "calcs": [ + "max" + ], + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [], + "values": false + }, + "orientation": "horizontal", + "showUnfilled": true + }, + "pluginVersion": "6.7.3", + "targets": [ + { + "expr": "sum(dapr_runtime_actor_status_report_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, operation)", + "legendFormat": "{{operation}} ({{app_id}})", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Report status error reasons", + "type": "bargauge" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This shows the placement table update status based on the response from placement service. Daprd maintains the actor placement table in its memory. This table is periodically updated.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 10, + "x": 14, + "y": 18 + }, + "hiddenSeries": false, + "id": 61, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 1, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(dapr_runtime_actor_table_operation_recv_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, operation)", + "format": "time_series", + "intervalFactor": 3, + "legendFormat": "{{operation}} ({{app_id}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Placement table operation", + "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 + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This chart shows how many actors are deactivated.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 25 + }, + "hiddenSeries": false, + "id": 65, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 1, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_runtime_actor_deactivated_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}", + "legendFormat": "OK {{actor_type}}", + "refId": "A" + }, + { + "expr": "dapr_runtime_actor_deactivated_failed_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}", + "legendFormat": "Error {{actor_type}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor Deactivation", + "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 + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This shows when actor is rebalanced. This operation can make the activated actors deactivated and move actor to the other hosts.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 7, + "x": 9, + "y": 25 + }, + "hiddenSeries": false, + "id": 63, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 1, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_runtime_actor_rebalanced_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}", + "intervalFactor": 1, + "legendFormat": "{{app_id}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Actor table rebalancing", + "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 + } + } + ], + "title": "Actor", + "type": "row" + }, + { + "collapsed": true, + "datasource": "Dapr", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 50, + "panels": [ + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This chart shows when mTLS is initialized, which means root cert is loaded and workload cert is issued from sentry, when runtime starts.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 7, + "x": 0, + "y": 11 + }, + "hiddenSeries": false, + "id": 48, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(dapr_runtime_mtls_init_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", + "intervalFactor": 2, + "legendFormat": "OK {{app_id}}", + "refId": "A" + }, + { + "expr": "rate(dapr_runtime_mtls_init_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", + "intervalFactor": 2, + "legendFormat": "Error {{app_id}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "mTLS initialization status", + "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": false + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cacheTimeout": null, + "datasource": "Dapr", + "gridPos": { + "h": 8, + "w": 5, + "x": 7, + "y": 11 + }, + "id": 67, + "links": [], + "options": { + "displayMode": "basic", + "fieldOptions": { + "calcs": [ + "mean" + ], + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [], + "values": false + }, + "orientation": "horizontal", + "showUnfilled": true + }, + "pluginVersion": "6.6.2", + "targets": [ + { + "expr": "sum(dapr_runtime_mtls_init_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, reason)", + "legendFormat": "{{reason}} ({{app_id}})", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "mTLS Initialization Errors", + "type": "bargauge" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This chart shows when workload certificate is rotated.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 7, + "x": 12, + "y": 11 + }, + "hiddenSeries": false, + "id": 66, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(dapr_runtime_mtls_workload_cert_rotated_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", + "format": "time_series", + "instant": false, + "intervalFactor": 2, + "legendFormat": "OK {{app_id}}", + "refId": "A" + }, + { + "expr": "rate(dapr_runtime_mtls_workload_cert_rotated_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}[5m])", + "intervalFactor": 2, + "legendFormat": "Error {{app_id}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Workload certificate rotation status", + "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": false + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cacheTimeout": null, + "datasource": "Dapr", + "gridPos": { + "h": 8, + "w": 5, + "x": 19, + "y": 11 + }, + "id": 68, + "links": [], + "options": { + "displayMode": "basic", + "fieldOptions": { + "calcs": [ + "mean" + ], + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 10 + } + ] + } + }, + "overrides": [], + "values": false + }, + "orientation": "horizontal", + "showUnfilled": true + }, + "pluginVersion": "6.6.2", + "targets": [ + { + "expr": "sum(dapr_runtime_mtls_workload_cert_rotated_fail_total{app_id=~\"$dapr_app_id\", kubernetes_namespace=\"$namespace\"}) by (app_id, reason)", + "legendFormat": "{{reason}} ({{app_id}})", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Cert rotation error reasons", + "type": "bargauge" + } + ], + "title": "Security", + "type": "row" + } + ], + "refresh": "10s", + "schemaVersion": 22, + "style": "dark", + "tags": [ "autosetup" ], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "Dapr", + "definition": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", + "hide": 0, + "includeAll": false, + "index": -1, + "label": "NAMESPACE", + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(dapr_runtime_component_loaded,kubernetes_namespace)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "Dapr", + "definition": "label_values(dapr_runtime_component_loaded,app_id)", + "hide": 0, + "includeAll": false, + "index": -1, + "label": "APPID", + "multi": true, + "name": "dapr_app_id", + "options": [], + "query": "label_values(dapr_runtime_component_loaded,app_id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "browser", + "title": "Dapr Sidecar Dashboard", + "variables": { + "list": [] + }, + "version": 0 + }, + "overwrite": true } \ No newline at end of file diff --git a/setup/config/system-services-dashboard.json b/setup/config/system-services-dashboard.json index 0677dd9..e97b2b2 100644 --- a/setup/config/system-services-dashboard.json +++ b/setup/config/system-services-dashboard.json @@ -1,1540 +1,1540 @@ -{ - "dashboard": { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": null, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 18, - "panels": [], - "title": "Health & Resource", - "type": "row" - }, - { - "cacheTimeout": null, - "datasource": "Dapr", - "gridPos": { - "h": 8, - "w": 4, - "x": 0, - "y": 1 - }, - "id": 20, - "links": [], - "options": { - "colorMode": "value", - "fieldOptions": { - "calcs": [ - "last" - ], - "defaults": { - "decimals": 1, - "mappings": [ - { - "id": 0, - "op": "=", - "text": "N/A", - "type": 1, - "value": "null" - } - ], - "nullValueMode": "connected", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "green", - "value": 600 - } - ] - }, - "unit": "s" - }, - "overrides": [], - "values": false - }, - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal" - }, - "pluginVersion": "6.6.2", - "targets": [ - { - "expr": "time() - max(process_start_time_seconds{app=~\"dapr-sentry|dapr-placement|dapr-sidecar-injector|dapr-operator\"}) by (app)", - "intervalFactor": 2, - "legendFormat": "{{app}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Uptime", - "type": "stat" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This shows total amount of kernel and user CPU usage time.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 7, - "x": 4, - "y": 1 - }, - "hiddenSeries": false, - "id": 22, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{pod=~\"(dapr-sentry|dapr-sidecar-injector|dapr-placement|dapr-operator).*\"}[5m])) by (pod)", - "legendFormat": "{{pod}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Total CPU usage (kernel and user)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "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": "Dapr", - "description": "The amount of memory that belongs specifically to that process in bytes. This excludes swapped out memory pages.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 7, - "x": 11, - "y": 1 - }, - "hiddenSeries": false, - "id": 24, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "max(process_resident_memory_bytes{app=~\"(dapr-sentry|dapr-sidecar-injector|dapr-placement|dapr-operator)\"}) by (app)", - "legendFormat": "{{app}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory usage in bytes", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "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": "Dapr", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 6, - "x": 18, - "y": 1 - }, - "hiddenSeries": false, - "id": 26, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "max(go_goroutines{app=~\"(dapr-sentry|dapr-sidecar-injector|dapr-placement|dapr-operator)\"}) by (app)", - "legendFormat": "{{app}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Number of GO routines", - "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 - } - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 9 - }, - "id": 14, - "panels": [], - "title": "Operator", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "The total number of services created.", - "fill": 10, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 10 - }, - "hiddenSeries": false, - "id": 6, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": true, - "targets": [ - { - "expr": "count(dapr_operator_service_created_total) by (app_id)", - "legendFormat": "{{app_id}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "# Services Created", - "tooltip": { - "shared": true, - "sort": 1, - "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": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "The total number of services deleted.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 10 - }, - "hiddenSeries": false, - "id": 4, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_operator_service_deleted_total", - "legendFormat": "{{app_id}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "# Services Deleted", - "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 - } - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 18 - }, - "id": 12, - "panels": [], - "title": "Sidecar Injector", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "The total number of sidecar injection requests.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 19 - }, - "hiddenSeries": false, - "id": 8, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_injector_sidecar_injection_requests_total", - "legendFormat": "sidecars requests", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "# sidecar injection requests", - "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 - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "The total number of successful sidecar injection requests.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 19 - }, - "hiddenSeries": false, - "id": 10, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_injector_sidecar_injection_succeeded_total", - "legendFormat": "{{app_id}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "# successful sidecar injected", - "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 - } - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 28 - }, - "id": 42, - "panels": [], - "title": "CA Sentry", - "type": "row" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#F2495C", - "#FADE2A", - "#73BF69" - ], - "datasource": null, - "decimals": null, - "description": "", - "format": "s", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 7, - "w": 3, - "x": 0, - "y": 29 - }, - "id": 44, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "pluginVersion": "6.6.2", - "postfix": " left", - "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, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "min(dapr_sentry_issuercert_expiry_timestamp) - time()", - "refId": "A" - } - ], - "thresholds": "2628000, 5256000", - "timeFrom": "1m", - "timeShift": null, - "title": "Root/Issuer cert expiry", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "Certificate Signing Request ( CSR ) from Dapr runtime", - "fill": 0, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 9, - "x": 3, - "y": 29 - }, - "hiddenSeries": false, - "id": 34, - "legend": { - "alignAsTable": true, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "CSR Requests", - "color": "rgb(60, 33, 166)", - "dashes": true - }, - { - "alias": "CSR Success", - "color": "#73BF69" - }, - { - "alias": "CSR Failure", - "color": "#F2495C" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(dapr_sentry_cert_sign_request_received_total{app=\"dapr-sentry\"})", - "legendFormat": "CSR Requests", - "refId": "A" - }, - { - "expr": "sum(dapr_sentry_cert_sign_success_total{app=\"dapr-sentry\"})", - "instant": false, - "legendFormat": "CSR Success", - "refId": "B" - }, - { - "expr": "sum(dapr_sentry_cert_sign_failure_total{app=\"dapr-sentry\"})", - "instant": false, - "legendFormat": "CSR Failure", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Certificate Signing Requests (CSR) from Daprd", - "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": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "This chart shows the failure reason of Certificate Sign Request.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 29 - }, - "hiddenSeries": false, - "id": 38, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(dapr_sentry_cert_sign_failure_total{app=\"dapr-sentry\"}) by (reason)", - "legendFormat": "{{reason}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CSR Failures", - "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": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "cacheTimeout": null, - "dashLength": 10, - "dashes": false, - "datasource": "Dapr", - "description": "This will be counted when issuer cert and key are changed.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 36 - }, - "hiddenSeries": false, - "id": 36, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.6.2", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(dapr_sentry_issuercert_changed_total{app=\"dapr-sentry\"})", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Issuer cert and key changed total", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "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": null, - "description": "This chart shows the reason of gRPC server TLS certificate issuance failures.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 36 - }, - "hiddenSeries": false, - "id": 40, - "legend": { - "alignAsTable": false, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(dapr_sentry_servercert_issue_failed_total{app=\"dapr-sentry\"}) by (reason)", - "legendFormat": "{{reason}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Server TLS certificate issuance failures", - "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": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 43 - }, - "id": 16, - "panels": [], - "title": "Placement", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "The total number of replicas connected to placement service.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 44 - }, - "hiddenSeries": false, - "id": 28, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_placement_hosts_total", - "legendFormat": "hosts", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "# total replicas", - "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 - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "The total number of replicas which are not hosting actors.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 44 - }, - "hiddenSeries": false, - "id": 30, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_placement_nonactorhosts_total", - "legendFormat": "{{kubernetes_pod_name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "# replicas not hosting actors", - "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 - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "The total number of actor types registered with Dapr runtime.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 52 - }, - "hiddenSeries": false, - "id": 32, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "dapr_placement_actortypes_total", - "legendFormat": "actor types", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "# actor types", - "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 - } - } - ], - "refresh": "15m", - "schemaVersion": 22, - "style": "dark", - "tags": [ "autosetup" ], - "templating": { - "list": [] - }, - "time": { - "from": "now-1h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "browser", - "title": "Dapr System Services Dashboard", - "version": 0 - }, -"overwrite": true +{ + "dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 18, + "panels": [], + "title": "Health & Resource", + "type": "row" + }, + { + "cacheTimeout": null, + "datasource": "Dapr", + "gridPos": { + "h": 8, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 20, + "links": [], + "options": { + "colorMode": "value", + "fieldOptions": { + "calcs": [ + "last" + ], + "defaults": { + "decimals": 1, + "mappings": [ + { + "id": 0, + "op": "=", + "text": "N/A", + "type": 1, + "value": "null" + } + ], + "nullValueMode": "connected", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 600 + } + ] + }, + "unit": "s" + }, + "overrides": [], + "values": false + }, + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal" + }, + "pluginVersion": "6.6.2", + "targets": [ + { + "expr": "time() - max(process_start_time_seconds{app=~\"dapr-sentry|dapr-placement|dapr-sidecar-injector|dapr-operator\"}) by (app)", + "intervalFactor": 2, + "legendFormat": "{{app}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Uptime", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This shows total amount of kernel and user CPU usage time.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 7, + "x": 4, + "y": 1 + }, + "hiddenSeries": false, + "id": 22, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{pod=~\"(dapr-sentry|dapr-sidecar-injector|dapr-placement|dapr-operator).*\"}[5m])) by (pod)", + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total CPU usage (kernel and user)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "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": "Dapr", + "description": "The amount of memory that belongs specifically to that process in bytes. This excludes swapped out memory pages.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 7, + "x": 11, + "y": 1 + }, + "hiddenSeries": false, + "id": 24, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(process_resident_memory_bytes{app=~\"(dapr-sentry|dapr-sidecar-injector|dapr-placement|dapr-operator)\"}) by (app)", + "legendFormat": "{{app}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory usage in bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "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": "Dapr", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 1 + }, + "hiddenSeries": false, + "id": 26, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(go_goroutines{app=~\"(dapr-sentry|dapr-sidecar-injector|dapr-placement|dapr-operator)\"}) by (app)", + "legendFormat": "{{app}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Number of GO routines", + "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 + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 14, + "panels": [], + "title": "Operator", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "The total number of services created.", + "fill": 10, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": true, + "targets": [ + { + "expr": "count(dapr_operator_service_created_total) by (app_id)", + "legendFormat": "{{app_id}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "# Services Created", + "tooltip": { + "shared": true, + "sort": 1, + "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": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "The total number of services deleted.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_operator_service_deleted_total", + "legendFormat": "{{app_id}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "# Services Deleted", + "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 + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 12, + "panels": [], + "title": "Sidecar Injector", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "The total number of sidecar injection requests.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 19 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_injector_sidecar_injection_requests_total", + "legendFormat": "sidecars requests", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "# sidecar injection requests", + "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 + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "The total number of successful sidecar injection requests.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 19 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_injector_sidecar_injection_succeeded_total", + "legendFormat": "{{app_id}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "# successful sidecar injected", + "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 + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 42, + "panels": [], + "title": "CA Sentry", + "type": "row" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#F2495C", + "#FADE2A", + "#73BF69" + ], + "datasource": null, + "decimals": null, + "description": "", + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 0, + "y": 29 + }, + "id": 44, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "pluginVersion": "6.6.2", + "postfix": " left", + "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, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "min(dapr_sentry_issuercert_expiry_timestamp) - time()", + "refId": "A" + } + ], + "thresholds": "2628000, 5256000", + "timeFrom": "1m", + "timeShift": null, + "title": "Root/Issuer cert expiry", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "Certificate Signing Request ( CSR ) from Dapr runtime", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 9, + "x": 3, + "y": 29 + }, + "hiddenSeries": false, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "CSR Requests", + "color": "rgb(60, 33, 166)", + "dashes": true + }, + { + "alias": "CSR Success", + "color": "#73BF69" + }, + { + "alias": "CSR Failure", + "color": "#F2495C" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(dapr_sentry_cert_sign_request_received_total{app=\"dapr-sentry\"})", + "legendFormat": "CSR Requests", + "refId": "A" + }, + { + "expr": "sum(dapr_sentry_cert_sign_success_total{app=\"dapr-sentry\"})", + "instant": false, + "legendFormat": "CSR Success", + "refId": "B" + }, + { + "expr": "sum(dapr_sentry_cert_sign_failure_total{app=\"dapr-sentry\"})", + "instant": false, + "legendFormat": "CSR Failure", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Certificate Signing Requests (CSR) from Daprd", + "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": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "This chart shows the failure reason of Certificate Sign Request.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 29 + }, + "hiddenSeries": false, + "id": 38, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(dapr_sentry_cert_sign_failure_total{app=\"dapr-sentry\"}) by (reason)", + "legendFormat": "{{reason}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CSR Failures", + "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": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "cacheTimeout": null, + "dashLength": 10, + "dashes": false, + "datasource": "Dapr", + "description": "This will be counted when issuer cert and key are changed.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 36 + }, + "hiddenSeries": false, + "id": 36, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pluginVersion": "6.6.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(dapr_sentry_issuercert_changed_total{app=\"dapr-sentry\"})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Issuer cert and key changed total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "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": null, + "description": "This chart shows the reason of gRPC server TLS certificate issuance failures.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 36 + }, + "hiddenSeries": false, + "id": 40, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(dapr_sentry_servercert_issue_failed_total{app=\"dapr-sentry\"}) by (reason)", + "legendFormat": "{{reason}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Server TLS certificate issuance failures", + "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": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 43 + }, + "id": 16, + "panels": [], + "title": "Placement", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "The total number of replicas connected to placement service.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 44 + }, + "hiddenSeries": false, + "id": 28, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_placement_hosts_total", + "legendFormat": "hosts", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "# total replicas", + "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 + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "The total number of replicas which are not hosting actors.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 44 + }, + "hiddenSeries": false, + "id": 30, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_placement_nonactorhosts_total", + "legendFormat": "{{kubernetes_pod_name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "# replicas not hosting actors", + "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 + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "The total number of actor types registered with Dapr runtime.", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 52 + }, + "hiddenSeries": false, + "id": 32, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "dapr_placement_actortypes_total", + "legendFormat": "actor types", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "# actor types", + "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 + } + } + ], + "refresh": "15m", + "schemaVersion": 22, + "style": "dark", + "tags": [ "autosetup" ], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "browser", + "title": "Dapr System Services Dashboard", + "version": 0 + }, +"overwrite": true } \ No newline at end of file diff --git a/setup/config/zipkin.yaml b/setup/config/zipkin.yaml index ee422db..ccf10fe 100644 --- a/setup/config/zipkin.yaml +++ b/setup/config/zipkin.yaml @@ -1,38 +1,38 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: zipkin - namespace: dapr-monitoring - labels: - app: zipkin -spec: - replicas: 1 - selector: - matchLabels: - app: zipkin - template: - metadata: - labels: - app: zipkin - spec: - containers: - - name: zipkin - image: openzipkin/zipkin - ports: - - containerPort: 9411 ---- -kind: Service -apiVersion: v1 -metadata: - name: zipkin - namespace: dapr-monitoring - labels: - app: zipkin -spec: - selector: - app: zipkin - ports: - - protocol: TCP - port: 9411 - targetPort: 9411 +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zipkin + namespace: dapr-monitoring + labels: + app: zipkin +spec: + replicas: 1 + selector: + matchLabels: + app: zipkin + template: + metadata: + labels: + app: zipkin + spec: + containers: + - name: zipkin + image: openzipkin/zipkin + ports: + - containerPort: 9411 +--- +kind: Service +apiVersion: v1 +metadata: + name: zipkin + namespace: dapr-monitoring + labels: + app: zipkin +spec: + selector: + app: zipkin + ports: + - protocol: TCP + port: 9411 + targetPort: 9411 type: ClusterIP \ No newline at end of file diff --git a/setup/gke/Makefile b/setup/gke/Makefile index f2ffe29..d06de75 100644 --- a/setup/gke/Makefile +++ b/setup/gke/Makefile @@ -1,65 +1,65 @@ -CLUSTER_NAME ?=demo -CLUSTER_ZONE ?=us-west1-a -CLUSTER_VERSION ?=1.17.13-gke.600 -NODE_COUNT ?=2 -NODE_TYPE ?=n2-standard-4 - -.PHONY: all -all: - @echo === ACTIVE CONFIGURATION === - @echo "CLUSTER_NAME: ${CLUSTER_NAME}" - @echo "CLUSTER_ZONE: ${CLUSTER_ZONE}" - @echo "CLUSTER_VERSION: ${CLUSTER_VERSION}" - @echo "NODE_COUNT: ${NODE_COUNT}" - @echo "NODE_TYPE: ${NODE_TYPE}" - @echo - @echo "Export these as environment variables to change their values" - @echo - - -.PHONY: setup -setup: ## Enables GKE APIs - gcloud services enable \ - cloudapis.googleapis.com \ - container.googleapis.com \ - containerregistry.googleapis.com - -.PHONY: cluster-list -cluster-list: ## List GKE clusters - gcloud container clusters list - -.PHONY: version-list -version-list: ## List Kubernetes versions supported in GKE - gcloud container get-server-config - -.PHONY: cluster -cluster: setup ## Create GKE cluster - # Create cluster - gcloud container clusters create $(CLUSTER_NAME) \ - --addons=HorizontalPodAutoscaling,HttpLoadBalancing \ - --zone $(CLUSTER_ZONE) \ - --node-locations $(CLUSTER_ZONE) \ - --cluster-version $(CLUSTER_VERSION) \ - --machine-type $(NODE_TYPE) \ - --num-nodes $(NODE_COUNT) \ - --enable-ip-alias \ - --enable-stackdriver-kubernetes \ - --enable-autorepair \ - --scopes cloud-platform - # Get cluster credentials - gcloud container clusters get-credentials $(CLUSTER_NAME) \ - --zone $(CLUSTER_ZONE) - # Create cluster binding for current user - kubectl create clusterrolebinding cluster-admin-binding \ - --clusterrole=cluster-admin \ - --user=$(gcloud config get-value core/account) - -.PHONY: cluster-down -cluster-down: cluster-list ## Delete previously created GKE cluster (make clusterdown CLUSTER_NAME=demo) - gcloud container clusters delete $(CLUSTER_NAME) \ - --zone $(CLUSTER_ZONE) - -.PHONY: help -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +CLUSTER_NAME ?=demo +CLUSTER_ZONE ?=us-west1-a +CLUSTER_VERSION ?=1.17.13-gke.600 +NODE_COUNT ?=2 +NODE_TYPE ?=n2-standard-4 + +.PHONY: all +all: + @echo === ACTIVE CONFIGURATION === + @echo "CLUSTER_NAME: ${CLUSTER_NAME}" + @echo "CLUSTER_ZONE: ${CLUSTER_ZONE}" + @echo "CLUSTER_VERSION: ${CLUSTER_VERSION}" + @echo "NODE_COUNT: ${NODE_COUNT}" + @echo "NODE_TYPE: ${NODE_TYPE}" + @echo + @echo "Export these as environment variables to change their values" + @echo + + +.PHONY: setup +setup: ## Enables GKE APIs + gcloud services enable \ + cloudapis.googleapis.com \ + container.googleapis.com \ + containerregistry.googleapis.com + +.PHONY: cluster-list +cluster-list: ## List GKE clusters + gcloud container clusters list + +.PHONY: version-list +version-list: ## List Kubernetes versions supported in GKE + gcloud container get-server-config + +.PHONY: cluster +cluster: setup ## Create GKE cluster + # Create cluster + gcloud container clusters create $(CLUSTER_NAME) \ + --addons=HorizontalPodAutoscaling,HttpLoadBalancing \ + --zone $(CLUSTER_ZONE) \ + --node-locations $(CLUSTER_ZONE) \ + --cluster-version $(CLUSTER_VERSION) \ + --machine-type $(NODE_TYPE) \ + --num-nodes $(NODE_COUNT) \ + --enable-ip-alias \ + --enable-stackdriver-kubernetes \ + --enable-autorepair \ + --scopes cloud-platform + # Get cluster credentials + gcloud container clusters get-credentials $(CLUSTER_NAME) \ + --zone $(CLUSTER_ZONE) + # Create cluster binding for current user + kubectl create clusterrolebinding cluster-admin-binding \ + --clusterrole=cluster-admin \ + --user=$(gcloud config get-value core/account) + +.PHONY: cluster-down +cluster-down: cluster-list ## Delete previously created GKE cluster (make clusterdown CLUSTER_NAME=demo) + gcloud container clusters delete $(CLUSTER_NAME) \ + --zone $(CLUSTER_ZONE) + +.PHONY: help +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/setup/gke/README.md b/setup/gke/README.md index 31c3ce1..5460a1c 100644 --- a/setup/gke/README.md +++ b/setup/gke/README.md @@ -1,63 +1,63 @@ -# GKE Cluster Setup - -The following parameters can be used to configure your deployment. Define these as environment variables to set or override the default value: - -```shell -CLUSTER_NAME # default: demo -CLUSTER_ZONE # default: us-west1-a -CLUSTER_VERSION # default: 1.17.13-gke.600 -NODE_COUNT # default: 2 -NODE_COUNT_MIN # default: 1 -NODE_COUNT_MAX # default: 5 -NODE_TYPE # default: n2-standard-4 -``` - -```shell -gcloud config set project -gcloud config set compute/region -gcloud config set compute/zone -``` - -## Usage - -Start by navigating to the [setup/gke](./setup/gke) directory - -> Run `make` by itself to see the active configuration - -* `make cluster` to create a cluster on GKE (make cluster CLUSTER_NAME=demo) -* `make cluster-list` to list your AKS clusters -* `make version-list` to list Kubernetes versions supported on AKS - -Once cluster is created, you can follow [these instructions](../) to configure Dapr. - -## Cleanup - -To lists previously created clusters run - -```shell -make cluster-list -``` - -To delete any of the previously created clusters run - -> yes, there will be a prompt to confirm before deleting - -```shell -make cluster-down CLUSTER_NAME=name -``` - -## Help - -To find the list of all the commands with their short descriptions run: - -```shell -make help -``` - -## Disclaimer - -This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. - -## License - +# GKE Cluster Setup + +The following parameters can be used to configure your deployment. Define these as environment variables to set or override the default value: + +```shell +CLUSTER_NAME # default: demo +CLUSTER_ZONE # default: us-west1-a +CLUSTER_VERSION # default: 1.17.13-gke.600 +NODE_COUNT # default: 2 +NODE_COUNT_MIN # default: 1 +NODE_COUNT_MAX # default: 5 +NODE_TYPE # default: n2-standard-4 +``` + +```shell +gcloud config set project +gcloud config set compute/region +gcloud config set compute/zone +``` + +## Usage + +Start by navigating to the [setup/gke](./setup/gke) directory + +> Run `make` by itself to see the active configuration + +* `make cluster` to create a cluster on GKE (make cluster CLUSTER_NAME=demo) +* `make cluster-list` to list your AKS clusters +* `make version-list` to list Kubernetes versions supported on AKS + +Once cluster is created, you can follow [these instructions](../) to configure Dapr. + +## Cleanup + +To lists previously created clusters run + +```shell +make cluster-list +``` + +To delete any of the previously created clusters run + +> yes, there will be a prompt to confirm before deleting + +```shell +make cluster-down CLUSTER_NAME=name +``` + +## Help + +To find the list of all the commands with their short descriptions run: + +```shell +make help +``` + +## Disclaimer + +This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code. + +## License + This software is released under the [MIT](../../LICENSE) \ No newline at end of file diff --git a/setup/ingress.yaml b/setup/ingress.yaml new file mode 100644 index 0000000..b244c64 --- /dev/null +++ b/setup/ingress.yaml @@ -0,0 +1,21 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: ingress-rules + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + tls: + - hosts: + - '*.mfussell.com' + - mfussell.com + secretName: tls-secret + rules: + - host: api.mfussell.com + http: + paths: + - path: / + backend: + serviceName: nginx-ingress-dapr + servicePort: 80 \ No newline at end of file diff --git a/setup/ingress2.yaml b/setup/ingress2.yaml new file mode 100644 index 0000000..0765048 --- /dev/null +++ b/setup/ingress2.yaml @@ -0,0 +1,22 @@ + apiVersion: networking.k8s.io/v1beta1 + kind: Ingress + metadata: + annotations: + kubernetes.io/ingress.class: nginx + name: example + namespace: foo + spec: + rules: + - host: mfussell.com + http: + paths: + - backend: + serviceName: exampleService + servicePort: 80 + path: / + # This section is only required if TLS is to be enabled for the Ingress + tls: + - hosts: + - www.example.com + secretName: example-tls + \ No newline at end of file diff --git a/setup/kubectl-install-dapr b/setup/kubectl-install-dapr index 2c68d04..f193ae4 100755 --- a/setup/kubectl-install-dapr +++ b/setup/kubectl-install-dapr @@ -1,119 +1,119 @@ -#!/bin/bash - -set -o errexit -set -o pipefail -set -o nounset - -declare -r DAPR_RELEASE=${DAPR_RELEASE:-"1.0.0-rc.1"} -declare -r DAPR_HA=${DAPR_HA:-"false"} -declare -r DAPR_LOG_AS_JSON=${DAPR_LOG_AS_JSON:-"false"} -declare -r DAPR_CONFIG=${DAPR_CONFIG:-"https://raw.githubusercontent.com/mchmarny/dapr-demos/master/setup/config"} - -echo "Configuration:" -echo "DAPR_RELEASE=${DAPR_RELEASE}" -echo "DAPR_HA=${DAPR_HA}" -echo "DAPR_LOG_AS_JSON=${DAPR_LOG_AS_JSON}" -echo "DAPR_CONFIG=${DAPR_CONFIG}" - -if pgrep kubectl &> /dev/null ; then pkill kubectl -9 ; fi - -# Installing Dapr... -helm repo add dapr https://dapr.github.io/helm-charts/ -helm repo update -kubectl create ns dapr-system -helm install dapr dapr/dapr -n dapr-system \ - --version $DAPR_RELEASE \ - --set global.logAsJson=$DAPR_LOG_AS_JSON \ - --set global.ha.enabled=$DAPR_HA -# Wait for everything to finish installing -kubectl rollout status deployment/dapr-operator -n dapr-system -kubectl rollout status deployment/dapr-dashboard -n dapr-system -kubectl rollout status deployment/dapr-sentry -n dapr-system -kubectl rollout status deployment/dapr-sidecar-injector -n dapr-system - -# Installing Keda -helm repo add kedacore https://kedacore.github.io/charts -helm repo update -kubectl create ns keda -helm install keda kedacore/keda -n keda --set logLevel=debug -# Wait for everything to finish installing -kubectl rollout status deployment/keda-operator -n keda -kubectl rollout status deployment/keda-operator-metrics-apiserver -n keda - -# Installing observabiliity... -helm repo add grafana https://grafana.github.io/helm-charts -helm repo add elastic https://helm.elastic.co -helm repo add prometheus https://prometheus-community.github.io/helm-charts -helm repo update -kubectl create ns dapr-monitoring -kubectl apply \ - -f "${DAPR_CONFIG}/fluentd-config.yaml" \ - -f "${DAPR_CONFIG}/fluentd.yaml" -kubectl apply -f "${DAPR_CONFIG}/zipkin.yaml" -n dapr-monitoring -helm install elasticsearch elastic/elasticsearch -n dapr-monitoring -helm install dapr-prom prometheus/prometheus -n dapr-monitoring -helm install grafana grafana/grafana -n dapr-monitoring \ - --set persistence.enabled=true \ - --set persistence.accessModes={ReadWriteOnce} \ - --set persistence.size=8Gi -helm install kibana elastic/kibana -n dapr-monitoring - -# Wait for everything to be ready... -kubectl rollout status deployment/dapr-prom-kube-state-metrics -n dapr-monitoring -kubectl rollout status deployment/dapr-prom-prometheus-alertmanager -n dapr-monitoring -kubectl rollout status deployment/dapr-prom-prometheus-pushgateway -n dapr-monitoring -kubectl rollout status deployment/dapr-prom-prometheus-server -n dapr-monitoring -kubectl rollout status deployment/grafana -n dapr-monitoring -kubectl rollout status deployment/kibana-kibana -n dapr-monitoring - -# Configure grafana dashbaords -declare -r GRAFANA_PASS=$(kubectl get secret \ - -n dapr-monitoring grafana \ - -o jsonpath="{.data.admin-password}" \ - | base64 --decode) - -kubectl port-forward svc/grafana 8888:80 -n dapr-monitoring & -curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ - -H "Content-Type: application/json" \ - -d '{ "name":"Dapr", "type":"prometheus", "url":"http://dapr-prom-prometheus-server.dapr-monitoring", "access":"proxy", "basicAuth":false }' \ - http://localhost:8888/api/datasources - -declare -r DASH1="system-services-dashboard.json" -if [ ! -f "$DASH1" ]; then - curl -O "${DAPR_CONFIG}/${DASH1}" -fi -curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ - -H "Content-Type: application/json" \ - -d @$DASH1 \ - http://localhost:8888/api/dashboards/db - -declare -r DASH2="sidecar-dashboard.json" -if [ ! -f "$DASH2" ]; then - curl -O "${DAPR_CONFIG}/${DASH2}" -fi -curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ - -H "Content-Type: application/json" \ - -d @sidecar-dashboard.json \ - http://localhost:8888/api/dashboards/db - -declare -r DASH3="actor-dashboard.json" -if [ ! -f "$DASH3" ]; then - curl -O "${DAPR_CONFIG}/${DASH3}" -fi -curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ - -H "Content-Type: application/json" \ - -d @actor-dashboard.json \ - http://localhost:8888/api/dashboards/db - -# Configure kibana -kubectl port-forward svc/kibana-kibana 5601 -n dapr-monitoring & -curl -X POST -H "kbn-xsrf: true" \ - -H "Content-Type: application/json" \ - -d '{"attributes":{"title":"dapr*","timeFieldName":"@timestamp"}}' \ - "http://localhost:5601/api/saved_objects/index-pattern/dapr" - -curl -X POST -H "kbn-xsrf: true" \ - -H "Content-Type: application/json" \ - -d '{"value":"dapr"}' \ - "http://localhost:5601/api/kibana/settings/defaultIndex" - +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +declare -r DAPR_RELEASE=${DAPR_RELEASE:-"1.0.0-rc.1"} +declare -r DAPR_HA=${DAPR_HA:-"false"} +declare -r DAPR_LOG_AS_JSON=${DAPR_LOG_AS_JSON:-"false"} +declare -r DAPR_CONFIG=${DAPR_CONFIG:-"https://raw.githubusercontent.com/mchmarny/dapr-demos/master/setup/config"} + +echo "Configuration:" +echo "DAPR_RELEASE=${DAPR_RELEASE}" +echo "DAPR_HA=${DAPR_HA}" +echo "DAPR_LOG_AS_JSON=${DAPR_LOG_AS_JSON}" +echo "DAPR_CONFIG=${DAPR_CONFIG}" + +if pgrep kubectl &> /dev/null ; then pkill kubectl -9 ; fi + +# Installing Dapr... +helm repo add dapr https://dapr.github.io/helm-charts/ +helm repo update +kubectl create ns dapr-system +helm install dapr dapr/dapr -n dapr-system \ + --version $DAPR_RELEASE \ + --set global.logAsJson=$DAPR_LOG_AS_JSON \ + --set global.ha.enabled=$DAPR_HA +# Wait for everything to finish installing +kubectl rollout status deployment/dapr-operator -n dapr-system +kubectl rollout status deployment/dapr-dashboard -n dapr-system +kubectl rollout status deployment/dapr-sentry -n dapr-system +kubectl rollout status deployment/dapr-sidecar-injector -n dapr-system + +# Installing Keda +helm repo add kedacore https://kedacore.github.io/charts +helm repo update +kubectl create ns keda +helm install keda kedacore/keda -n keda --set logLevel=debug +# Wait for everything to finish installing +kubectl rollout status deployment/keda-operator -n keda +kubectl rollout status deployment/keda-operator-metrics-apiserver -n keda + +# Installing observabiliity... +helm repo add grafana https://grafana.github.io/helm-charts +helm repo add elastic https://helm.elastic.co +helm repo add prometheus https://prometheus-community.github.io/helm-charts +helm repo update +kubectl create ns dapr-monitoring +kubectl apply \ + -f "${DAPR_CONFIG}/fluentd-config.yaml" \ + -f "${DAPR_CONFIG}/fluentd.yaml" +kubectl apply -f "${DAPR_CONFIG}/zipkin.yaml" -n dapr-monitoring +helm install elasticsearch elastic/elasticsearch -n dapr-monitoring +helm install dapr-prom prometheus/prometheus -n dapr-monitoring +helm install grafana grafana/grafana -n dapr-monitoring \ + --set persistence.enabled=true \ + --set persistence.accessModes={ReadWriteOnce} \ + --set persistence.size=8Gi +helm install kibana elastic/kibana -n dapr-monitoring + +# Wait for everything to be ready... +kubectl rollout status deployment/dapr-prom-kube-state-metrics -n dapr-monitoring +kubectl rollout status deployment/dapr-prom-prometheus-alertmanager -n dapr-monitoring +kubectl rollout status deployment/dapr-prom-prometheus-pushgateway -n dapr-monitoring +kubectl rollout status deployment/dapr-prom-prometheus-server -n dapr-monitoring +kubectl rollout status deployment/grafana -n dapr-monitoring +kubectl rollout status deployment/kibana-kibana -n dapr-monitoring + +# Configure grafana dashbaords +declare -r GRAFANA_PASS=$(kubectl get secret \ + -n dapr-monitoring grafana \ + -o jsonpath="{.data.admin-password}" \ + | base64 --decode) + +kubectl port-forward svc/grafana 8888:80 -n dapr-monitoring & +curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ + -H "Content-Type: application/json" \ + -d '{ "name":"Dapr", "type":"prometheus", "url":"http://dapr-prom-prometheus-server.dapr-monitoring", "access":"proxy", "basicAuth":false }' \ + http://localhost:8888/api/datasources + +declare -r DASH1="system-services-dashboard.json" +if [ ! -f "$DASH1" ]; then + curl -O "${DAPR_CONFIG}/${DASH1}" +fi +curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ + -H "Content-Type: application/json" \ + -d @$DASH1 \ + http://localhost:8888/api/dashboards/db + +declare -r DASH2="sidecar-dashboard.json" +if [ ! -f "$DASH2" ]; then + curl -O "${DAPR_CONFIG}/${DASH2}" +fi +curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ + -H "Content-Type: application/json" \ + -d @sidecar-dashboard.json \ + http://localhost:8888/api/dashboards/db + +declare -r DASH3="actor-dashboard.json" +if [ ! -f "$DASH3" ]; then + curl -O "${DAPR_CONFIG}/${DASH3}" +fi +curl -X POST -s -k -u "admin:${GRAFANA_PASS}" \ + -H "Content-Type: application/json" \ + -d @actor-dashboard.json \ + http://localhost:8888/api/dashboards/db + +# Configure kibana +kubectl port-forward svc/kibana-kibana 5601 -n dapr-monitoring & +curl -X POST -H "kbn-xsrf: true" \ + -H "Content-Type: application/json" \ + -d '{"attributes":{"title":"dapr*","timeFieldName":"@timestamp"}}' \ + "http://localhost:5601/api/saved_objects/index-pattern/dapr" + +curl -X POST -H "kbn-xsrf: true" \ + -H "Content-Type: application/json" \ + -d '{"value":"dapr"}' \ + "http://localhost:5601/api/kibana/settings/defaultIndex" + diff --git a/state-change-handler/Dockerfile b/state-change-handler/Dockerfile index f5904ac..a5c01c7 100644 --- a/state-change-handler/Dockerfile +++ b/state-change-handler/Dockerfile @@ -1,14 +1,14 @@ -FROM golang:1.15.0 as builder - -WORKDIR /src/ -COPY . /src/ - -ENV GO111MODULE=on - -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -a -tags netgo -mod vendor -o ./service . - -FROM gcr.io/distroless/static:nonroot -COPY --from=builder /src/service . - -ENTRYPOINT ["./service"] +FROM golang:1.15.0 as builder + +WORKDIR /src/ +COPY . /src/ + +ENV GO111MODULE=on + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -a -tags netgo -mod vendor -o ./service . + +FROM gcr.io/distroless/static:nonroot +COPY --from=builder /src/service . + +ENTRYPOINT ["./service"] diff --git a/state-change-handler/Makefile b/state-change-handler/Makefile index 9886579..b2a1b16 100644 --- a/state-change-handler/Makefile +++ b/state-change-handler/Makefile @@ -1,53 +1,53 @@ -RELEASE_VERSION =v0.11.1 -SERVICE_NAME ?="publisher" -DOCKER_USERNAME ?=$(DOCKER_USER) - -.PHONY: mod test run debug build dapr event image show imagerun lint clean, tag, init -all: test - -tidy: ## Updates the go modules and vendors all dependencies - go mod tidy - go mod vendor - -test: tidy ## Tests the entire project - go test -count=1 -race ./... - -build: tidy ## Builds local release binary - CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . - -debug: ## Runs uncompiled code it in Dapr in debug mode - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-protocol grpc \ - --app-port 50001 \ - --components-path ./config \ - --log-level debug \ - go run main.go - -run: build ## Builds binary and runs it in Dapr - dapr run \ - --app-id $(SERVICE_NAME) \ - --app-protocol grpc \ - --app-port 50001 \ - --components-path ./config \ - bin/$(SERVICE_NAME) - -image: tidy ## Builds and publish docker image - docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . - docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" - -lint: ## Lints the entire project - golangci-lint run --timeout=3m - -tag: ## Creates release tag - git tag $(RELEASE_VERSION) - git push origin $(RELEASE_VERSION) - -clean: ## Cleans up generated files - go clean - rm -fr ./bin - rm -fr ./vendor - -help: ## Display available commands - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ - 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +RELEASE_VERSION =v0.11.1 +SERVICE_NAME ?="publisher" +DOCKER_USERNAME ?=$(DOCKER_USER) + +.PHONY: mod test run debug build dapr event image show imagerun lint clean, tag, init +all: test + +tidy: ## Updates the go modules and vendors all dependencies + go mod tidy + go mod vendor + +test: tidy ## Tests the entire project + go test -count=1 -race ./... + +build: tidy ## Builds local release binary + CGO_ENABLED=0 go build -a -tags netgo -mod vendor -o bin/$(SERVICE_NAME) . + +debug: ## Runs uncompiled code it in Dapr in debug mode + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-protocol grpc \ + --app-port 50001 \ + --components-path ./config \ + --log-level debug \ + go run main.go + +run: build ## Builds binary and runs it in Dapr + dapr run \ + --app-id $(SERVICE_NAME) \ + --app-protocol grpc \ + --app-port 50001 \ + --components-path ./config \ + bin/$(SERVICE_NAME) + +image: tidy ## Builds and publish docker image + docker build -t "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" . + docker push "$(DOCKER_USERNAME)/$(SERVICE_NAME):$(RELEASE_VERSION)" + +lint: ## Lints the entire project + golangci-lint run --timeout=3m + +tag: ## Creates release tag + git tag $(RELEASE_VERSION) + git push origin $(RELEASE_VERSION) + +clean: ## Cleans up generated files + go clean + rm -fr ./bin + rm -fr ./vendor + +help: ## Display available commands + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk \ + 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/state-change-handler/README.md b/state-change-handler/README.md index 1e417f6..59c8811 100644 --- a/state-change-handler/README.md +++ b/state-change-handler/README.md @@ -1,47 +1,47 @@ -# Dapr State Change Publisher - -Binding to detect RethinkDB state changes and stream them into a single topic. - -## Components - -### Source - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: changes -spec: - type: bindings.rethinkdb.statechange - metadata: - - name: address - value: "127.0.0.1:28015" - - name: database - value: "dapr" -``` - -### Target - -```yaml -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" -``` - -## Service - -```shell -dapr run --app-id publisher \ - --protocol grpc \ - --app-port 50001 \ - --components-path ./config \ - go run main.go +# Dapr State Change Publisher + +Binding to detect RethinkDB state changes and stream them into a single topic. + +## Components + +### Source + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: changes +spec: + type: bindings.rethinkdb.statechange + metadata: + - name: address + value: "127.0.0.1:28015" + - name: database + value: "dapr" +``` + +### Target + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" +``` + +## Service + +```shell +dapr run --app-id publisher \ + --protocol grpc \ + --app-port 50001 \ + --components-path ./config \ + go run main.go ``` \ No newline at end of file diff --git a/state-change-handler/config/pubsub.yaml b/state-change-handler/config/pubsub.yaml index 0531def..841b884 100644 --- a/state-change-handler/config/pubsub.yaml +++ b/state-change-handler/config/pubsub.yaml @@ -1,11 +1,11 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: events -spec: - type: pubsub.redis - metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: events +spec: + type: pubsub.redis + metadata: + - name: redisHost + value: localhost:6379 + - name: redisPassword + value: "" diff --git a/state-change-handler/config/statechange.yaml b/state-change-handler/config/statechange.yaml index 0d7923b..7b05013 100644 --- a/state-change-handler/config/statechange.yaml +++ b/state-change-handler/config/statechange.yaml @@ -1,17 +1,17 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: changes -spec: - type: bindings.rethinkdb.statechange - metadata: - - name: address - value: "127.0.0.1:28015" - - name: database - value: "dapr" - - name: table - value: "daprstate" - - name: username - value: "admin" # default - - name: password +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: changes +spec: + type: bindings.rethinkdb.statechange + metadata: + - name: address + value: "127.0.0.1:28015" + - name: database + value: "dapr" + - name: table + value: "daprstate" + - name: username + value: "admin" # default + - name: password value: "rethinkdb" # default \ No newline at end of file diff --git a/state-change-handler/go.mod b/state-change-handler/go.mod index d2a608d..b68e926 100644 --- a/state-change-handler/go.mod +++ b/state-change-handler/go.mod @@ -1,8 +1,8 @@ -module github.com/mchmarny/dapr-demos/state-change-handler - -go 1.15 - -require ( - github.com/dapr/go-sdk v0.11.0 - github.com/pkg/errors v0.9.1 -) +module github.com/mchmarny/dapr-demos/state-change-handler + +go 1.15 + +require ( + github.com/dapr/go-sdk v0.11.0 + github.com/pkg/errors v0.9.1 +) diff --git a/state-change-handler/go.sum b/state-change-handler/go.sum index 6bf079a..77d8561 100644 --- a/state-change-handler/go.sum +++ b/state-change-handler/go.sum @@ -1,100 +1,100 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= -github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= -golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= -google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/dapr/go-sdk v0.11.0 h1:oUAWkFvOevvT+CwvjdIXs4fvK1Gjs33ni0tdHLFcqIo= +github.com/dapr/go-sdk v0.11.0/go.mod h1:hre4W06eUYUDzWZBo+ZkOJdjnzuW9GZwZBRYOymvmvs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20 h1:4X356008q5SA3YXu8PiRap39KFmy4Lf6sGlceJKZQsU= +golang.org/x/sys v0.0.0-20200917073148-efd3b9a0ff20/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0 h1:uslsjIdqvZYANxSBQjTI47vZfwMaTN3mLELkMnMIY/A= +google.golang.org/genproto v0.0.0-20200917134801-bb4cff56e0d0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/state-change-handler/main.go b/state-change-handler/main.go index 2136e5d..449b4b9 100755 --- a/state-change-handler/main.go +++ b/state-change-handler/main.go @@ -1,67 +1,67 @@ -package main - -import ( - "context" - "log" - "os" - "strings" - - dapr "github.com/dapr/go-sdk/client" - "github.com/dapr/go-sdk/service/common" - daprd "github.com/dapr/go-sdk/service/grpc" - "github.com/pkg/errors" -) - -var ( - client dapr.Client - logger = log.New(os.Stdout, "", 0) - address = getEnvVar("ADDRESS", ":50001") - method = getEnvVar("METHOD", "changes") - pubsub = getEnvVar("PUBSUB", "events") - topic = getEnvVar("TOPIC", "changes") -) - -func main() { - // create client - c, err := dapr.NewClient() - if err != nil { - logger.Fatalf("error creating Dapr client: %v", err) - } - client = c - defer client.Close() - - // create the service - s, err := daprd.NewService(address) - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } - - // add method handler - err = s.AddBindingInvocationHandler(method, bindingHandler) - if err != nil { - log.Fatalf("error adding binding handler: %v", err) - } - - // start the service - if err := s.Start(); err != nil { - log.Fatalf("server error: %v", err) - } -} - -func bindingHandler(ctx context.Context, in *common.BindingEvent) (out []byte, err error) { - log.Printf("binding - Data:%s, Meta:%v", in.Data, in.Metadata) - if in.Data != nil || len(in.Data) > 0 { - if err := client.PublishEvent(ctx, pubsub, topic, in.Data); err != nil { - logger.Printf("error publishing data to topic: %s", topic) - return nil, errors.Wrapf(err, "error publishing data to topic: %s", topic) - } - } - return nil, nil -} - -func getEnvVar(key, fallbackValue string) string { - if val, ok := os.LookupEnv(key); ok { - return strings.TrimSpace(val) - } - return fallbackValue -} +package main + +import ( + "context" + "log" + "os" + "strings" + + dapr "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/grpc" + "github.com/pkg/errors" +) + +var ( + client dapr.Client + logger = log.New(os.Stdout, "", 0) + address = getEnvVar("ADDRESS", ":50001") + method = getEnvVar("METHOD", "changes") + pubsub = getEnvVar("PUBSUB", "events") + topic = getEnvVar("TOPIC", "changes") +) + +func main() { + // create client + c, err := dapr.NewClient() + if err != nil { + logger.Fatalf("error creating Dapr client: %v", err) + } + client = c + defer client.Close() + + // create the service + s, err := daprd.NewService(address) + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + + // add method handler + err = s.AddBindingInvocationHandler(method, bindingHandler) + if err != nil { + log.Fatalf("error adding binding handler: %v", err) + } + + // start the service + if err := s.Start(); err != nil { + log.Fatalf("server error: %v", err) + } +} + +func bindingHandler(ctx context.Context, in *common.BindingEvent) (out []byte, err error) { + log.Printf("binding - Data:%s, Meta:%v", in.Data, in.Metadata) + if in.Data != nil || len(in.Data) > 0 { + if err := client.PublishEvent(ctx, pubsub, topic, in.Data); err != nil { + logger.Printf("error publishing data to topic: %s", topic) + return nil, errors.Wrapf(err, "error publishing data to topic: %s", topic) + } + } + return nil, nil +} + +func getEnvVar(key, fallbackValue string) string { + if val, ok := os.LookupEnv(key); ok { + return strings.TrimSpace(val) + } + return fallbackValue +}