mirror of https://github.com/dapr/samples.git
dapr apim integration sample
This commit is contained in:
parent
6d4d960c9f
commit
4918cfeb7f
|
@ -0,0 +1,16 @@
|
|||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependencies
|
||||
vendor
|
||||
bin
|
|
@ -0,0 +1,653 @@
|
|||
# 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.
|
||||
|
||||

|
||||
|
||||
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/)
|
||||
|
||||
> Note, this demo has been only tested with v0.10 release of Dapr
|
||||
|
||||
## 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://<YOUR-APIM-SERVICE-NAME>.azure-api.net
|
||||
- url: https://<YOUR-APIM-SERVICE-NAME>.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
|
||||
<policies>
|
||||
<inbound>
|
||||
<rate-limit-by-key
|
||||
calls="120"
|
||||
renewal-period="60"
|
||||
increment-condition="@(context.Response.StatusCode == 200)"
|
||||
counter-key="@(context.Request.IpAddress)" />
|
||||
</inbound>
|
||||
...
|
||||
</policies>
|
||||
```
|
||||
|
||||
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/<appId>/method/<method-name>
|
||||
```
|
||||
|
||||
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 (`<base />`) 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
|
||||
<policies>
|
||||
<inbound>
|
||||
<base />
|
||||
<set-backend-service
|
||||
backend-id="dapr"
|
||||
dapr-app-id="echo-service"
|
||||
dapr-method="echo" />
|
||||
</inbound>
|
||||
...
|
||||
</policies>
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
#### 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/<pubsubname>/<topic>
|
||||
```
|
||||
|
||||
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
|
||||
<policies>
|
||||
<inbound>
|
||||
<base />
|
||||
<publish-to-dapr
|
||||
topic="@("demo-events/messages")"
|
||||
response-variable-name="pubsub-response"
|
||||
>@(context.Request.Body.As<string>())</publish-to-dapr>
|
||||
<return-response
|
||||
response-variable-name="pubsub-response" />
|
||||
</inbound>
|
||||
...
|
||||
</policies>
|
||||
```
|
||||
|
||||
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/<name>
|
||||
```
|
||||
|
||||
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
|
||||
<policies>
|
||||
<inbound>
|
||||
<base />
|
||||
<set-variable name="key"
|
||||
value="@{ return Guid.NewGuid().ToString(); }" />
|
||||
<invoke-dapr-binding
|
||||
name="demo-binding"
|
||||
operation="create"
|
||||
response-variable-name="binding-response">
|
||||
<metadata>
|
||||
<item key="source">APIM</item>
|
||||
<item key="client-ip">@( context.Request.IpAddress )</item>
|
||||
<item key="key">@( (string)context.Variables["key"] )</item>
|
||||
</metadata>
|
||||
<data>@( context.Request.Body.As<string>() )</data>
|
||||
</invoke-dapr-binding>
|
||||
<return-response response-variable-name="binding-response" />
|
||||
</inbound>
|
||||
...
|
||||
</policies>
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
```
|
||||
|
||||
### 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/id: "event-subscriber"
|
||||
dapr.io/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 pre-build Docker images of two applications: [gRPC Echo Service](src/grpc-echo-service) and [HTTP Event Subscriber](src/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](https://docs.microsoft.com/en-us/azure/api-management/how-to-deploy-self-hosted-gateway-kubernetes) to [APIM service on Kubernetes](https://docs.microsoft.com/en-us/azure/api-management/api-management-kubernetes), we will need to create a 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 helpful 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
## Updating Dapr Components
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
||||
|
|
@ -0,0 +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':
|
||||
description: ''
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"properties": {
|
||||
"format": "xml",
|
||||
"value": "<policies><inbound><rate-limit-by-key calls=\"120\" renewal-period=\"60\" increment-condition=\"@(context.Response.StatusCode == 200)\" counter-key=\"@(context.Request.IpAddress)\" /></inbound><backend><forward-request /></backend><outbound /><on-error /></policies>"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"properties": {
|
||||
"format": "xml",
|
||||
"value": "<policies><inbound><base /><set-backend-service backend-id=\"dapr\" dapr-app-id=\"echo-service\" dapr-method=\"echo\" /></inbound><backend><base /></backend><outbound></outbound><on-error><base /></on-error></policies>"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"properties": {
|
||||
"format": "xml",
|
||||
"value": "<policies><inbound><base /><publish-to-dapr topic=\"@("demo-events/messages")\" response-variable-name=\"pubsub-response\">@( context.Request.Body.As<string>() )</publish-to-dapr><return-response response-variable-name=\"pubsub-response\" /></inbound><backend /><outbound><base /></outbound><on-error><base /></on-error></policies>"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"properties": {
|
||||
"format": "xml",
|
||||
"value": "<policies><inbound><base /><set-variable name=\"key\" value=\"@{ return Guid.NewGuid().ToString(); }\" /><invoke-dapr-binding name=\"demo-binding\" operation=\"create\" response-variable-name=\"binding-response\"><metadata><item key=\"source\">APIM</item><item key=\"client-ip\">@( context.Request.IpAddress )</item><item key=\"key\">@( (string)context.Variables[\"key\"] )</item></metadata><data>@( context.Request.Body.As<string>() )</data></invoke-dapr-binding><return-response response-variable-name=\"binding-response\" /></inbound><backend /><outbound><base /></outbound><on-error><base /></on-error></policies>"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
After Width: | Height: | Size: 193 KiB |
Binary file not shown.
After Width: | Height: | Size: 313 KiB |
|
@ -0,0 +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
|
||||
key: redis-password
|
|
@ -0,0 +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.14.0
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 60002
|
||||
env:
|
||||
- name: ADDRESS
|
||||
value: ":60002"
|
|
@ -0,0 +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/id: "event-subscriber"
|
||||
dapr.io/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.14.0
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: PORT
|
||||
value: "8080"
|
||||
- name: PUBSUB_NAME
|
||||
value: "demo-events"
|
||||
- name: TOPIC_NAME
|
||||
value: "messages"
|
|
@ -0,0 +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:
|
||||
app: demo-apim-gateway
|
|
@ -0,0 +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
|
||||
value: "messages"
|
|
@ -0,0 +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"]
|
|
@ -0,0 +1,56 @@
|
|||
RELEASE_VERSION =v0.14.0
|
||||
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: 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}'
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/dapr/samples/dapr-apim-integration
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/dapr/go-sdk v0.10.0
|
|
@ -0,0 +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.10.0 h1:SzQtNf2yRNtU8xVOcuTqzyOv+NDWCrqeTdJD6TDKQHk=
|
||||
github.com/dapr/go-sdk v0.10.0/go.mod h1:62T7zLYXKaIBw+aawzVBIJX2CesGeUHg9C6EWpwiKLw=
|
||||
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-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/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-20200810151505-1b9f1253b3ed h1:WBkVNH1zd9jg/dK4HCM4lNANnmd12EHC9z+LmcCG4ns=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/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-20200808173500-a06252235341 h1:Kceb+1TNS2X7Cj/A+IUTljNerF/4wOFjlFJ0RGHYKKE=
|
||||
google.golang.org/genproto v0.0.0-20200808173500-a06252235341/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.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
|
||||
google.golang.org/grpc v1.31.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=
|
|
@ -0,0 +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
|
||||
}
|
|
@ -0,0 +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"]
|
|
@ -0,0 +1,58 @@
|
|||
RELEASE_VERSION =v0.14.0
|
||||
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
|
||||
|
||||
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 '<message><from>John</from><to>Lary</to></message>' \
|
||||
-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)"
|
||||
|
||||
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}'
|
|
@ -0,0 +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: ""
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/dapr/samples/dapr-apim-integration
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/dapr/go-sdk v0.10.0
|
|
@ -0,0 +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.10.0 h1:SzQtNf2yRNtU8xVOcuTqzyOv+NDWCrqeTdJD6TDKQHk=
|
||||
github.com/dapr/go-sdk v0.10.0/go.mod h1:62T7zLYXKaIBw+aawzVBIJX2CesGeUHg9C6EWpwiKLw=
|
||||
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-20200707034311-ab3426394381/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-20200810151505-1b9f1253b3ed/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-20200808173500-a06252235341/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.31.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=
|
|
@ -0,0 +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) 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 nil
|
||||
}
|
||||
|
||||
func getEnvVar(key, fallbackValue string) string {
|
||||
if val, ok := os.LookupEnv(key); ok {
|
||||
return strings.TrimSpace(val)
|
||||
}
|
||||
return fallbackValue
|
||||
}
|
Loading…
Reference in New Issue