From 5dd192c75bc4500c54b7a9dddb295c5ecc1b8c10 Mon Sep 17 00:00:00 2001 From: Bilgin Ibryam Date: Tue, 21 May 2024 09:55:14 +0100 Subject: [PATCH 01/92] Fixes for: Wrong instructions in How to: Use the Multi-App Run template file #4153 --- .../multi-app-dapr-run/multi-app-template.md | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md b/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md index e5c526da4..c37115311 100644 --- a/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md +++ b/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md @@ -1,6 +1,6 @@ --- type: docs -title: "How to: Use the Multi-App Run template file" +title: "q" linkTitle: "How to: Use the Multi-App Run template" weight: 2000 description: Unpack the Multi-App Run template file and its properties @@ -42,7 +42,7 @@ dapr run -f ```cmd -dapr run -f -k +dapr run -f -k ``` {{% /codetab %}} @@ -67,7 +67,7 @@ dapr run -f ./path/to/.yaml ```cmd -dapr run -f -k ./path/to/.yaml +dapr run -f ./path/to/.yaml -k ``` {{% /codetab %}} @@ -77,10 +77,27 @@ dapr run -f -k ./path/to/.yaml Once the multi-app template is running, you can view the started applications with the following command: +{{< tabs Self-hosted Kubernetes>}} + +{{% codetab %}} + + ```cmd dapr list ``` +{{% /codetab %}} + +{{% codetab %}} + + +```cmd +dapr list -k +``` +{{% /codetab %}} + +{{< /tabs >}} + ## Stop the multi-app template Stop the multi-app run template anytime with either of the following commands: @@ -109,12 +126,12 @@ dapr stop -f ./path/to/.yaml ```cmd # the template file needs to be called `dapr.yaml` by default if a directory path is given -dapr stop -f -k +dapr stop -f -k ``` or: ```cmd -dapr stop -f -k ./path/to/.yaml +dapr stop -f ./path/to/.yaml -k ``` {{% /codetab %}} From 893df1f841ee0675d25b92360db7c560ad8a7697 Mon Sep 17 00:00:00 2001 From: Bilgin Ibryam Date: Tue, 21 May 2024 09:59:42 +0100 Subject: [PATCH 02/92] Fixed acceidental title change --- .../local-development/multi-app-dapr-run/multi-app-template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md b/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md index c37115311..606ae9fbe 100644 --- a/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md +++ b/daprdocs/content/en/developing-applications/local-development/multi-app-dapr-run/multi-app-template.md @@ -1,6 +1,6 @@ --- type: docs -title: "q" +title: "How to: Use the Multi-App Run template file" linkTitle: "How to: Use the Multi-App Run template" weight: 2000 description: Unpack the Multi-App Run template file and its properties From 8129bd156d4c661ea194cd800729803da5e308d8 Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Thu, 30 May 2024 15:56:47 +0200 Subject: [PATCH 03/92] Describe queue-name, policy and dead-letter queue aspects Describe how to get past stumbling-blocks I met when setting up a service-bus queue pubsub. Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index 57e3b9286..a8d07cd4f 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -181,6 +181,17 @@ When subscribing to a topic, you can configure `bulkSubscribe` options. Refer to Follow the instructions [here](https://learn.microsoft.com/azure/service-bus-messaging/service-bus-quickstart-portal) on setting up Azure Service Bus Queues. +{{% alert title="Note" color="primary" %}} +Your queue will need to have the same name as the Topic you are publishing to with Dapr. E.g. if you are publishing to the pubsb "myPubsub" on the topic "orders" your queue must be named "orders". +If you are using a Shared access policy to connect to the queue that policy must have the ability to "Manage" the queue. To work with a dead-letter queue the policy must live on the Service Bus Namespace that contains both the main queue and the dead-letter queue. +{{% /alert %}} + +### Retry policy and dead-letter queues + +An Azure ServiceBus Queue has a dead-letter quue by default. If you do not modify it the message will be retried the "Max delivery count" number of times (defaults to 10, can be set up to 2000). These retries will happen with very little delay before the message is put in the dead-letter queue. + +Since Dapr Pubsub has its own dead-letter queue -concept you can use this instead if you need control over retry-policy and you need to subscribe to the dead-letter queue (you probably should) you should set up a separate queue as that dead-letter queue in the same namespace, and a resilience component that defines how to retry on input. + ## Related links - [Basic schema for a Dapr component]({{< ref component-schema >}}) From e912c50d01d51550f9787c1fa20eaab4b876af52 Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Thu, 30 May 2024 16:02:04 +0200 Subject: [PATCH 04/92] Update setup-azure-servicebus-queues.md Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index a8d07cd4f..ac428de2a 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -190,7 +190,7 @@ If you are using a Shared access policy to connect to the queue that policy must An Azure ServiceBus Queue has a dead-letter quue by default. If you do not modify it the message will be retried the "Max delivery count" number of times (defaults to 10, can be set up to 2000). These retries will happen with very little delay before the message is put in the dead-letter queue. -Since Dapr Pubsub has its own dead-letter queue -concept you can use this instead if you need control over retry-policy and you need to subscribe to the dead-letter queue (you probably should) you should set up a separate queue as that dead-letter queue in the same namespace, and a resilience component that defines how to retry on input. +Since Dapr Pubsub has its own dead-letter queue -concept you can use this instead if you need control over retry-policy and you need to subscribe to the dead-letter queue (you probably should). Set up a separate queue as that dead-letter queue in the same namespace, and a resilience component that defines how to retry on input. Then subscribe to that queue (topic) to get the failed messages and deal with them. ## Related links From b924132113ef153ffd7414bacdfb10f6650afbd3 Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Thu, 30 May 2024 16:12:06 +0200 Subject: [PATCH 05/92] Update setup-azure-servicebus-queues.md Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index ac428de2a..7057c27c4 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -182,15 +182,15 @@ When subscribing to a topic, you can configure `bulkSubscribe` options. Refer to Follow the instructions [here](https://learn.microsoft.com/azure/service-bus-messaging/service-bus-quickstart-portal) on setting up Azure Service Bus Queues. {{% alert title="Note" color="primary" %}} -Your queue will need to have the same name as the Topic you are publishing to with Dapr. E.g. if you are publishing to the pubsb "myPubsub" on the topic "orders" your queue must be named "orders". +Your queue must have the same name as the Topic you are publishing to with Dapr. E.g. if you are publishing to the pubsb "myPubsub" on the topic "orders" your queue must be named "orders". If you are using a Shared access policy to connect to the queue that policy must have the ability to "Manage" the queue. To work with a dead-letter queue the policy must live on the Service Bus Namespace that contains both the main queue and the dead-letter queue. {{% /alert %}} ### Retry policy and dead-letter queues -An Azure ServiceBus Queue has a dead-letter quue by default. If you do not modify it the message will be retried the "Max delivery count" number of times (defaults to 10, can be set up to 2000). These retries will happen with very little delay before the message is put in the dead-letter queue. +An Azure ServiceBus Queue has a dead-letter queue by default. By default the messages are retried the "Max delivery count" number of times (defaults to 10, can be set up to 2000). These retries happen very rapidly and the message is put in the dead-letter queue if no success is returned. -Since Dapr Pubsub has its own dead-letter queue -concept you can use this instead if you need control over retry-policy and you need to subscribe to the dead-letter queue (you probably should). Set up a separate queue as that dead-letter queue in the same namespace, and a resilience component that defines how to retry on input. Then subscribe to that queue (topic) to get the failed messages and deal with them. +Dapr Pubsub has its own dead-letter queue -concept you can use instead. This lets you control the retry-policy and you can subscribe to the dead-letter queue through Dapr. Set up a separate queue as that dead-letter queue in the Azure Service Bus Namespace, and a resilience component that defines how to retry. You can subscribe to that (topic) to get the failed messages and deal with them. E.g. setting up a dead-letter queue `orders-dlq` in the subscription and a resilience lets your subscribe to the topic `orders-dlq` to handle failed messages. ## Related links From 1d91ccd727a5ddc4d0a7644920b6bd9a7f814f7d Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Fri, 31 May 2024 00:09:59 +0200 Subject: [PATCH 06/92] change resilience component to policy and link pubsub-deadletter Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index 7057c27c4..fc6d95915 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -190,7 +190,9 @@ If you are using a Shared access policy to connect to the queue that policy must An Azure ServiceBus Queue has a dead-letter queue by default. By default the messages are retried the "Max delivery count" number of times (defaults to 10, can be set up to 2000). These retries happen very rapidly and the message is put in the dead-letter queue if no success is returned. -Dapr Pubsub has its own dead-letter queue -concept you can use instead. This lets you control the retry-policy and you can subscribe to the dead-letter queue through Dapr. Set up a separate queue as that dead-letter queue in the Azure Service Bus Namespace, and a resilience component that defines how to retry. You can subscribe to that (topic) to get the failed messages and deal with them. E.g. setting up a dead-letter queue `orders-dlq` in the subscription and a resilience lets your subscribe to the topic `orders-dlq` to handle failed messages. +Dapr Pubsub has its own dead-letter queue -concept you can use instead. This lets you control the retry-policy and you can subscribe to the dead-letter queue through Dapr. Set up a separate queue as that dead-letter queue in the Azure Service Bus Namespace, and a resilience policy that defines how to retry. You can subscribe to that (topic) to get the failed messages and deal with them. E.g. setting up a dead-letter queue `orders-dlq` in the subscription and a resilience lets your subscribe to the topic `orders-dlq` to handle failed messages. + +For more details on setting up dead-letter queues, see the [dead-letter article]({{< ref pubsub-deadletter >}}). ## Related links From db646a0ed77af44113b01e8c4b71e42aa7054bd6 Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Sun, 30 Jun 2024 20:28:46 +0200 Subject: [PATCH 07/92] Update daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index cb9a30954..20057ee35 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -190,7 +190,11 @@ If you are using a Shared access policy to connect to the queue that policy must An Azure ServiceBus Queue has a dead-letter queue by default. By default the messages are retried the "Max delivery count" number of times (defaults to 10, can be set up to 2000). These retries happen very rapidly and the message is put in the dead-letter queue if no success is returned. -Dapr Pubsub has its own dead-letter queue -concept you can use instead. This lets you control the retry-policy and you can subscribe to the dead-letter queue through Dapr. Set up a separate queue as that dead-letter queue in the Azure Service Bus Namespace, and a resilience policy that defines how to retry. You can subscribe to that (topic) to get the failed messages and deal with them. E.g. setting up a dead-letter queue `orders-dlq` in the subscription and a resilience lets your subscribe to the topic `orders-dlq` to handle failed messages. +Dapr Pub/sub offers its own dead-letter queue concept that lets you control the retry policy and subscribe to the dead-letter queue through Dapr. +1. Set up a separate queue as that dead-letter queue in the Azure Service Bus namespace, and a resilience policy that defines how to retry. +1. Subscribe to the topic to get the failed messages and deal with them. + +For example, setting up a dead-letter queue `orders-dlq` in the subscription and a resiliency policy lets you subscribe to the topic `orders-dlq` to handle failed messages. For more details on setting up dead-letter queues, see the [dead-letter article]({{< ref pubsub-deadletter >}}). From 1fed5a0922469f9a33f88b953d55b90f93e5d1ab Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Sun, 30 Jun 2024 20:28:57 +0200 Subject: [PATCH 08/92] Update daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index 20057ee35..fe25df682 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -183,7 +183,7 @@ Follow the instructions [here](https://learn.microsoft.com/azure/service-bus-mes {{% alert title="Note" color="primary" %}} Your queue must have the same name as the Topic you are publishing to with Dapr. E.g. if you are publishing to the pubsb "myPubsub" on the topic "orders" your queue must be named "orders". -If you are using a Shared access policy to connect to the queue that policy must have the ability to "Manage" the queue. To work with a dead-letter queue the policy must live on the Service Bus Namespace that contains both the main queue and the dead-letter queue. +If you are using a shared access policy to connect to the queue, that policy must be able to "manage" the queue. To work with a dead-letter queue, the policy must live on the Service Bus Namespace that contains both the main queue and the dead-letter queue. {{% /alert %}} ### Retry policy and dead-letter queues From 74a87a7c3f62717328df9bfe5f1a670059881889 Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Sun, 30 Jun 2024 20:29:05 +0200 Subject: [PATCH 09/92] Update daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index fe25df682..baf093625 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -182,7 +182,7 @@ When subscribing to a topic, you can configure `bulkSubscribe` options. Refer to Follow the instructions [here](https://learn.microsoft.com/azure/service-bus-messaging/service-bus-quickstart-portal) on setting up Azure Service Bus Queues. {{% alert title="Note" color="primary" %}} -Your queue must have the same name as the Topic you are publishing to with Dapr. E.g. if you are publishing to the pubsb "myPubsub" on the topic "orders" your queue must be named "orders". +Your queue must have the same name as the topic you are publishing to with Dapr. For example, if you are publishing to the pub/sub `"myPubsub"` on the topic `"orders"`, your queue must be named `"orders"`. If you are using a shared access policy to connect to the queue, that policy must be able to "manage" the queue. To work with a dead-letter queue, the policy must live on the Service Bus Namespace that contains both the main queue and the dead-letter queue. {{% /alert %}} From b7587634afd7e2c82e601bcba0c50ff568f1b54d Mon Sep 17 00:00:00 2001 From: Tomas Ekeli Date: Sun, 30 Jun 2024 20:29:13 +0200 Subject: [PATCH 10/92] Update daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Tomas Ekeli --- .../supported-pubsub/setup-azure-servicebus-queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index baf093625..2bc7ca5f9 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -188,7 +188,7 @@ If you are using a shared access policy to connect to the queue, that policy mus ### Retry policy and dead-letter queues -An Azure ServiceBus Queue has a dead-letter queue by default. By default the messages are retried the "Max delivery count" number of times (defaults to 10, can be set up to 2000). These retries happen very rapidly and the message is put in the dead-letter queue if no success is returned. +By default, an Azure Service Bus Queue has a dead-letter queue. The messages are retried the amount given for `maxDeliveryCount`. The default `maxDeliveryCount` value defaults to 10, but can be set up to 2000. These retries happen very rapidly and the message is put in the dead-letter queue if no success is returned. Dapr Pub/sub offers its own dead-letter queue concept that lets you control the retry policy and subscribe to the dead-letter queue through Dapr. 1. Set up a separate queue as that dead-letter queue in the Azure Service Bus namespace, and a resilience policy that defines how to retry. From 4b172d57838bf1e2f5eccbcc6b3038a4e09d73f4 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 8 Jul 2024 12:44:39 -0400 Subject: [PATCH 11/92] add screenshots with scheduler service Signed-off-by: Hannah Hunter --- .../getting-started/install-dapr-selfhost.md | 32 +++++++----------- .../en/reference/cli/dapr-uninstall.md | 4 +-- .../dapr-init-output.png | Bin 0 -> 36770 bytes .../docker-containers.png | Bin 21405 -> 23295 bytes 4 files changed, 14 insertions(+), 22 deletions(-) create mode 100644 daprdocs/static/images/install-dapr-selfhost/dapr-init-output.png diff --git a/daprdocs/content/en/getting-started/install-dapr-selfhost.md b/daprdocs/content/en/getting-started/install-dapr-selfhost.md index fc82e73ca..b851a3c25 100644 --- a/daprdocs/content/en/getting-started/install-dapr-selfhost.md +++ b/daprdocs/content/en/getting-started/install-dapr-selfhost.md @@ -95,28 +95,11 @@ dapr init **Expected output:** -``` -⌛ Making the jump to hyperspace... -✅ Downloaded binaries and completed components set up. -ℹ️ daprd binary has been installed to $HOME/.dapr/bin. -ℹ️ dapr_placement container is running. -ℹ️ dapr_scheduler container is running. -ℹ️ dapr_redis container is running. -ℹ️ dapr_zipkin container is running. -ℹ️ Use `docker ps` to check running containers. -✅ Success! Dapr is up and running. To get started, go here: https://aka.ms/dapr-getting-started -``` + [See the troubleshooting guide if you encounter any error messages regarding Docker not being installed or running.]({{< ref "common_issues.md#dapr-cant-connect-to-docker-when-installing-the-dapr-cli" >}}) -#### Slim init - -To install the CLI without any default configuration files or Docker containers, use the `--slim` flag. [Learn more about the `init` command and its flags.]({{< ref dapr-init.md >}}) - -```bash -dapr init --slim -``` - ### Step 3: Verify Dapr version ```bash @@ -138,7 +121,7 @@ docker ps **Output:** - + ### Step 5: Verify components directory has been initialized @@ -189,5 +172,14 @@ explorer "%USERPROFILE%\.dapr"
+### Slim init + +To install the CLI without any default configuration files or Docker containers, use the `--slim` flag. [Learn more about the `init` command and its flags.]({{< ref dapr-init.md >}}) + +```bash +dapr init --slim +``` + + {{< button text="Next step: Use the Dapr API >>" page="getting-started/get-started-api.md" >}} diff --git a/daprdocs/content/en/reference/cli/dapr-uninstall.md b/daprdocs/content/en/reference/cli/dapr-uninstall.md index cf9091c04..877987820 100644 --- a/daprdocs/content/en/reference/cli/dapr-uninstall.md +++ b/daprdocs/content/en/reference/cli/dapr-uninstall.md @@ -24,10 +24,10 @@ dapr uninstall [flags] | Name | Environment Variable | Default | Description | | -------------------- | -------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--all` | | `false` | Remove Redis, Zipkin containers in addition to the scheduler service and the actor placement container. Remove default dapr dir located at `$HOME/.dapr or %USERPROFILE%\.dapr\`. | +| `--all` | | `false` | Remove Redis, Zipkin containers in addition to the Scheduler service and the actor Placement service containers. Remove default Dapr dir located at `$HOME/.dapr or %USERPROFILE%\.dapr\`. | | `--help`, `-h` | | | Print this help message | | `--kubernetes`, `-k` | | `false` | Uninstall Dapr from a Kubernetes cluster | -| `--namespace`, `-n` | | `dapr-system` | The Kubernetes namespace to uninstall Dapr from | +| `--namespace`, `-n` | | `dapr-system` | The Kubernetes namespace from which Dapr is uninstalled | | `--container-runtime` | | `docker` | Used to pass in a different container runtime other than Docker. Supported container runtimes are: `docker`, `podman` | ### Examples diff --git a/daprdocs/static/images/install-dapr-selfhost/dapr-init-output.png b/daprdocs/static/images/install-dapr-selfhost/dapr-init-output.png new file mode 100644 index 0000000000000000000000000000000000000000..e3fde1fddc9be9139af12bf35ae1a5f2ffedbe17 GIT binary patch literal 36770 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.e{9d%8i1<6^?MA&dakH(u zcER$n_P^nVXloHK&Y65&%Pi;!f%dW>UZC56lmzh8>|(#Yvo#aJDsH$XG`LhN&TTRu zRU4MyHUTO(9E;o|htvUH!=Pqh@P<2i!8Z{A9_ePRkAZ;_quB2P?~<8W{Z8<>|Mz<; zN;LG_6_>%)MC6U}8(kS-M9@exAPpy*iX9T5 z`)3C-p7NcX0)KsR3o#{el5_inhm$X#Vq(Iw%o|#sY^|=YR);SZ@Z^q9f>#M@$c+0d z1o-%&!66~Fi}2C&X&@~NyG4M7)bm5Cc z7SstspUl~7RB>cljBXXQmnF4X#X&kv_S}I|-4YkRv?pArKYml_K_$?fY5n~4nxnRw zsV!k}(8=3L*@?8(1;`b4&&m+(N?ADO1zw>Ihis}&bYp?qadp7Hf z`{CX}Jn|p^=nOtmuuG}(+4ST=V!+@ZHhH)zy6^06fP4#znCJT97#L))I>4|8p4pA? zsJF1dz4lAy7l+Q0mG%2Xb7JH1=Nea2v+mcf4SE^oN;OLbCO&xhkdBs?I2fh`tgNh1 zbFP5*U0P&ov4Mx>o~Zq>jJx}0u31Bj>vtOdIp8Je=38BZpjrz?@kcyA&>>_KTs6+8 z&dvto26vj0s?{{y>+kB3abfYpdxG-gHxQ_jM3T7e26PLn$Y#>3G z^lDD&@yor}*qfMPRLrB|*ICoY+x~l*%)-_5`Ewh-h)E(1YA9a_%g;9X_m^mp`B$bA zmpiE0cP_hM3flp+_R*S=)q<%XOM#nIIsztKk8?}CbK}#}tf1cp6>?@~1|k~g&!5Mg z&Od95OGwxTMqT8m0~jUc4r8<2YcOUzMSzPsiT-6DXJT9z+@?O)f&QqF^+-qUI$(wp zlcU?XA;nm|QPWiQ1D*_^nv>jM5Snms9ef(m4nOY2l(|koY5ssJ$xd~RCR5N&Dz>`hN)ls~p z^3=KciOkI*{ZcmyHLL{RKWrin$0derJwCH5?b#|^nyq#0W zq6XUI>~_Rmod&VXjyL*`Y@)cdnPr>AHo2rEix?S(2S6P_xP;#N6G|BcQob!N+fHWe zQWEJmn@MlwTQoCs=^?^B91CTDc;XO9wh&490^}(C6{Ao%BKc?o<_BHJk0WMhH$q5$ zVn`<(N5~1#avV4^Ad-8-ls`vEi2Muak*Rvc4_V_8QcCrwi{z;x9n$7tfac` zPvpFPy97smZpofsyCO($JtI9`x{D0J9kW|_Lf*as7l=JsiqCLzR&lbUelK3)}<}TPzzQl;?_E!R5Zx%s=LRoA=)_o~W3K#OXh;5tO2cuwT z7g9Lhy*mRTOElBQDLSje9png;r?T#Zw=eE4EghXbcCx}32(a_XL~~hx!iwNOw$MM7 zp?x%brYPP*PC+4H;CK*)6UzS1ZBxbmAovoyUdS*g)wwM&cXnL<(*U)ODCb=W7s3OQ zWg|fhQ(XifM_eObiRSE;qjw5Y5UhkKJTC)Oh%Z^X5+(KRO7|7Rmwb5Gn2v%}^yKPn z|D&#$rem0@B6iHpV_>%{Mjq#lHO;Av?`uPF1=A6L=2wq*tW~y^BubqO*5~6}2 zXX<#Kyq9EHCQ@?y%CDko&JL*VU{3S=ROg@US13XMnYAfPFK9QSHH$Y9xMu(FPA&l5tnEQM=^uJY5W&0NYFedX6qKH3SejzwBT>a>uPG%T3 zQl8PHhM3p-c&s6uXdntifS!XmtKqQ&rH|CLb2l4H>_>1qKp1%hCy(25>K*^@}3&!rx)RwiJsJkWu*=+8bY9yiSRNAvxeH;x!n(w)iyf1Ev&?*vp&+CnSw9 zmI-MHKUr8S1%T$x7f4i0xrXm%86`Wy5&kbT4EtvH&jYHde(TfgME5GwvKKFuq z=QxQF!+8g6CY0v_nKl=_XS-TQMphQ+@iGN4(z1WNBe?*)PDpP64i!KvESyxa*4EZz zi}@MTtCv#RH4)Cky=2305gw&G8$SYSMJ!9MTI;=Yo-_E^n=0vn*Hb;8Nj#*qmi$_L zykf!pMGX?@?qJHraBDy zxJ4xYQDfFey7vcSg0>X2gX1-)Z3O+UUkMok$xHP`Qf-)j*wKC&f_y^EVI;~gQp{*D z^n}J`xnq0&jX^027aLs<@!Dt5Pew!tnA8v!HT;`vb_Tpmd#SYnaNHx~{>GPdG&F&k z`(dyQLmi#x;o-|*S7MHnorGsN1Z3Ua+_I@`yeNq)P8aN3Vrk!+){~NF>f}##-;9+F zPqh#!_|(|wIa@TIoqP3>IZN;=iw|JvQ*S`~qxIl%oHNuHi!W__A%KvnS9DfDU?q?( z2CzT9L}EV=$OZ@k3pS1Ga!A1Yf7e^l3T~ewsP_jPBsx zr3?MHnkC?YLjIpMi+7lwStish0pRF_uKo_WBxP{5U!t{UK2A#dK(OHnK=#{8Olpro-WCq}k5*mB z1<^Jzw@*rie!5@&AOXpafS_Q)4fP}-AtNa>>{K`RY*kU@t!f3wmyR4BIBszF90;{SeK@$9xkr=%4%_=UD78}Yqx^=zYyUOLmvopzkDpR+WIg`; zV5>TgycaLoZr*2U^F3fMZeHG2s%xnfk5oGSG5K4!G9nrA7$>86h_ZCUiWl~NxlwP# zRl{B9wx-_3+ep{C-{#yQ&pSDn6MkD(@z09JBD%=aYY$u|!uw0TVXq5u5IrC+Bh>As zrgBV1O^w8hJQ#Dm>S|rdo4hcy=8De7RqJ*4TeV9t{r)v(2Gq4UFK=A!5&*7%<06qz zUS6J`-~RJqZ$Kneion!^$Q;_j+8SDGEf^-DMsk zV6*)oY(fpv&2=bhD6;#k3^(SY-`lyX-df`XH?+G90kXuRP1j;h_4ISBiGC;<)FMpP zm~iC5h5ALJTs80R56y9u^ZVB~DfBhTyXmAy!f+3Dfn!}9^Q{k2WTc(Pgq<1KJdL7clkYsKYW zB8ktj+nj{BxDvjg(WHK{X^?#I;oG-w&7nF4?P2uMlwGgJLP!7n*o5X1RZVb~CANApR=<{=C>? z9YHHl!~xjrBVE(S=bBVs|+atur?s{E2#`GKyaOOb9c{Sx}*kT zS;r?R5ljsgPH@S)tb=<~5j{Zo+DW+8oH`RoZy0>H6eM`A2Ngd85`j@UW~i5)uJ7o1 zYQV+fL)gQ!>d+e@M51pYbahl@H3zNa-hw`S_Uu`}%`_ixwO@*6s4~Hi7gP*ks}8Hf z{jD6*`e!-BduXWk&vGc@zbJ>IonJf(3i_yt`&8(5U%(1UDBG!1r9X!s1?Z?<@Yq;} zdk_+{)3Uv>`MVp2yPRoKx6a?4YP+^pcl_Ia;K(#thyQ?O9yUqVwR3v2{{BW39@>A? zQVS_Q!B&WXyDJ$He-uIzQ8^S->)##{srpzf?fWuUBY>q7FXw{+7bukSv;_(xyCMj3 z_Jp6E4eM&joX#w`$G}38&~r*DQeh<4V^v$G2aad(h$he=@eL*CuO}UEV34z!SeIWT zbyD}E{W{5vpByHhku{bb(LY(E({ao?YMY&&F>%PR4Vh` zZVcvn{Xz}KaBrQFFzE4ave-N8PpflHy&9go zS?}GM$94=?JK8%s(z3pu?y|sX*6Ky@tM1B576@De!|Z2V2Ayn$2CWwxwS#Wd>+X+2^r%7DSv(A(4u`DwP}%?d`4fx` z;N5~N!GWYDz*QczFYHW09g?<$a|Es<$W$=FDVaNtsnev*&Q||g?{)I>@OlNsr1SUU zUBwxM!20uV0;}kML14Y&DfbU|qP;LZm{)Xa#v`JlLdx=E9M28Mb$!^y4AX+{_iF)ztR`*kBOhs_%%yY~Ohg!!t)@8INvvHtW9= z{bV66DG8|bpBHyEH=n4_J@!kWiF+ytCud51`v0fKO5NP6u^N64rjzXl(^XHBsQstFqlPm73`+iq69>Kk>rsww`8d-A z!ge7^{8L0C=O_-sU-$_%)d=PyVpW$-2Lm*ui%LqhyYt&OBXxCBV^7Nv`tYGdK9Gc_ zX}^5XViFdCaB&dt=~_)x;xtTHWpgtL(a6NCM2r??&YlwdAb|#j7q_pjy<(3stKmQb z1u`;Xb%{;A?o`I-eTZ@CfT*UUr#J1;t-aeKY^?M#REwF4@a_>J1}?`m2!#<6>VX}f z(s&!ZicHPDy^oHnp*B_;UTMM{WdRAbxch(=R}rnJI{+tVTIJ$CHk8K~R5hdXtG-&w zgCq}56iBV3OJ^&G zV1Fx%il^9r$mY)2RjnO1V1#iim^ZP~Va~uilgrDU%jajpCyS}YEiPY?5Q#+`VkSw5 ziI00Mh!a3?4KJbg4Lk+Vt0?ApFJ9dT2tlkKU#9bkgv9V_VkJ26L12fX>B7CAyu<16 zz0LeU^OmWA&-0}2V3tJP$v`Nm6%Ra$2QVc%n0ka~Ih2TFy$3cEUe9>lu&pd-A5N$vu0Hw0;@8OU|rkRe8j;Bwl z72V(Ld8KZQ+8>&hYBr1aw}ZCM;$c$oS87-q(q5J~jMi{08r#`lpw+^$CnH@1ugDmB zal;~2`wCrepYr{j?@k~AlX180FrsKY^)deu(2De7?FbCORL`%1)EaCvOjYq=QQ4?uoKoE5u&D?pR1s$`uJIl|eh&gE z$vD%czXIb_31;q)a6G#=7CYo?1!J*hLYbeKkPCStRw@~{UY-RBa@YUm1V2M1s*o{iOPvX_oFbA>0Bnc1laG)0Tru*ET(4fGU%b88;pl40Yz#Z6b zqZ-10=-|PFNp)!rs!-X;B96|B!S5Z{fu2-@KXIZ!_S~rT5avmMLPAtGQqdoze7wN$ zG4YuEHCU{WnHg4^Sff^|*r$Lnvn!JilA2!wPJd~`g8g|sJPg{t*j>buLxiT3)PD#~ z7Srz_kFiT9AWgy=`&Phgb8UQv!+B@^q7(=BoOk%9SAZ@=Pt(idE-i;*%A`#a(f+;b zC4xz46pW+2e_-O%9xkd4KhwavaKe%C&E$Y+O`yfQ|8f9^NKSRqP$l7xprP6e3<-fj z3>Q#*($b!&V{YK?8eKYSud!z4m_e9-m2{UG=5^ZI|HErEaQ`R1T4bbs zOsfeVLw-pi@S}^T7jBr_^4mEADZ8M3ksoKzb|mJc;=_0Lc@dxD-U8K8g5Tw*XFh

9_Fj$Qx8LFtmz5DF@WMM-Ur;YL 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 literal 21405 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 -ha#n+O$~l$LBQ<%mMK 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 Date: Mon, 8 Jul 2024 16:31:34 -0400 Subject: [PATCH 12/92] revert to 1.13 features and remove 1.14 features Signed-off-by: Hannah Hunter --- .../configuration/configuration-overview.md | 17 +-- .../observability/metrics/metrics-overview.md | 130 +----------------- .../resource-specs/configuration-schema.md | 4 - 3 files changed, 7 insertions(+), 144 deletions(-) diff --git a/daprdocs/content/en/operations/configuration/configuration-overview.md b/daprdocs/content/en/operations/configuration/configuration-overview.md index 5b6a4d73d..af4f3f475 100644 --- a/daprdocs/content/en/operations/configuration/configuration-overview.md +++ b/daprdocs/content/en/operations/configuration/configuration-overview.md @@ -110,30 +110,17 @@ metrics: rules: [] http: increasedCardinality: true - pathMatching: - - /items - - /orders/{orderID} - - /orders/{orderID}/items/{itemID} - - /payments/{paymentID} - - /payments/{paymentID}/status - - /payments/{paymentID}/refund - - /payments/{paymentID}/details - excludeVerbs: false ``` -In the examples above, the path filter `/orders/{orderID}/items/{itemID}` would return a single metric count matching all the `orderIDs` and all the `itemIDs`, rather than multiple metrics for each `itemID`. For more information, see [HTTP metrics path matching]({{< ref "metrics-overview.md#http-metrics-path-matching" >}}). - The following table lists the properties for metrics: | Property | Type | Description | |--------------|--------|-------------| | `enabled` | boolean | When set to true, the default, enables metrics collection and the metrics endpoint. | | `rules` | array | Named rule to filter metrics. Each rule contains a set of `labels` to filter on and a `regex` expression to apply to the metrics path. | -| `http.increasedCardinality` | boolean | When set to `true` (default), in the Dapr HTTP server, each request path causes the creation of a new "bucket" of metrics. This can cause issues, including excessive memory consumption when there many different requested endpoints (such as when interacting with RESTful APIs).
To mitigate high memory usage and egress costs associated with [high cardinality metrics]({{< ref "metrics-overview.md#high-cardinality-metrics" >}}) with the HTTP server, you should set the `metrics.http.increasedCardinality` property to `false`.| -| `http.pathMatching` | array | Paths used for path matching, allowing users to define matching paths in order to manage cardinality. | -| `http.excludeVerbs` | boolean | When set to `true` (default is `false`), the Dapr HTTP server ignores each request HTTP verb when building the method metric label. | +| `http.increasedCardinality` | boolean | When set to true, in the Dapr HTTP server each request path causes the creation of a new "bucket" of metrics. This can cause issues, including excessive memory consumption, when there many different requested endpoints (such as when interacting with RESTful APIs).
In Dapr 1.13 the default value is `true` (to preserve the behavior of Dapr <= 1.12), but changes to `false` in Dapr 1.14..| -To further help managing cardinality, path matching allows specified paths matched according to defined patterns, reducing the number of unique metrics paths and thus controlling metric cardinality. This feature is particularly useful for applications with dynamic URLs, ensuring that metrics remain meaningful and manageable without excessive memory consumption. +To mitigate high memory usage and egress costs associated with [high cardinality metrics]({{< ref "metrics-overview.md#high-cardinality-metrics" >}}) with the HTTP server, you should set the `metrics.http.increasedCardinality` property to `false`. Using rules, you can set regular expressions for every metric exposed by the Dapr sidecar. For example: diff --git a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md index da2ec6fc3..e26453d09 100644 --- a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md +++ b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md @@ -70,135 +70,15 @@ spec: enabled: false ``` -## Optimizing HTTP metrics reporting with path matching +## High cardinality metrics -When invoking Dapr using HTTP, metrics are created for each requested method by default. This can result in a high number of metrics, known as high cardinality, which can impact memory usage and CPU. +When invoking Dapr using HTTP, the legacy behavior (and current default as of Dapr 1.13) is to create a separate "bucket" for each requested method. When working with RESTful APIs, this can cause very high cardinality, with potential negative impact on memory usage and CPU. -Path matching allows you to manage and control the cardinality of HTTP metrics in Dapr. This is an aggregation of metrics, so rather than having a metric for each event, you can reduce the number of metrics events and report an overall number. For details on how to set the cardinality in configuration see ({{< ref "configuration-overview.md#metrics" >}}) - -This configuration is opt-in and is enabled via the Dapr configuration `spec.metrics.http.pathMatching`. When defined, it enables path matching, which standardizes specified paths for both metrics paths. This reduces the number of unique metrics paths, making metrics more manageable and reducing resource consumption in a controlled way. - -When `spec.metrics.http.pathMatching` is combined with the `increasedCardinality` flag set to `false`, non-matched paths are transformed into a catch-all bucket to control and limit cardinality, preventing unbounded path growth. Conversely, when `increasedCardinality` is `true` (the default), non-matched paths are passed through as they normally would be, allowing for potentially higher cardinality but preserving the original path data. - -### Examples of Path Matching in HTTP Metrics - -The following examples demonstrate how to use the Path Matching API in Dapr for managing HTTP metrics. On each example, the metrics are collected from 5 HTTP requests to the `/orders` endpoint with different order IDs. By adjusting cardinality and utilizing path matching, you can fine-tune metric granularity to balance detail and resource efficiency. - -These examples illustrate the cardinality of the metrics, highlighting that high cardinality configurations result in many entries, which correspond to higher memory usage for handling metrics. For simplicity, the following example focuses on a single metric: `dapr_http_server_request_count`. - -#### Low cardinality with path matching (Recommendation) - -Configuration: -```yaml -http: - increasedCardinality: false - pathMatching: - - /orders/{orderID} -``` - -Metrics generated: -``` -# matched paths -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/{orderID}",status="200"} 5 -# unmatched paths -dapr_http_server_request_count{app_id="order-service",method="GET",path="",status="200"} 1 -``` - -With low cardinality and path matching configured, you get the best of both worlds by grouping the metrics for the important endpoints without compromising the cardinality. This approach helps avoid high memory usage and potential security issues. - -#### Low cardinality without path matching - -Configuration: - -```yaml -http: - increasedCardinality: false -``` -Metrics generated: -``` -dapr_http_server_request_count{app_id="order-service",method="GET", path="",status="200"} 5 -``` - -In low cardinality mode, the path, which is the main source of unbounded cardinality, is dropped. This results in metrics that primarily indicate the number of requests made to the service for a given HTTP method, but without any information about the paths invoked. - - -#### High cardinality with path matching - -Configuration: -```yaml -http: - increasedCardinality: true - pathMatching: - - /orders/{orderID} -``` - -Metrics generated: -``` -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/{orderID}",status="200"} 5 -``` - -This example results from the same HTTP requests as the example above, but with path matching configured for the path `/orders/{orderID}`. By using path matching, you achieve reduced cardinality by grouping the metrics based on the matched path. - -#### High Cardinality without path matching - -Configuration: -```yaml -http: - increasedCardinality: true -``` - -Metrics generated: -``` -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/1",status="200"} 1 -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/2",status="200"} 1 -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/3",status="200"} 1 -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/4",status="200"} 1 -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/5",status="200"} 1 -``` - -For each request, a new metric is created with the request path. This process continues for every request made to a new order ID, resulting in unbounded cardinality since the IDs are ever-growing. - - -### HTTP metrics exclude verbs - -The `excludeVerbs` option allows you to exclude specific HTTP verbs from being reported in the metrics. This can be useful in high-performance applications where memory savings are critical. - -### Examples of excluding HTTP verbs in metrics - -The following examples demonstrate how to exclude HTTP verbs in Dapr for managing HTTP metrics. - -#### Default - Include HTTP verbs - -Configuration: -```yaml -http: - excludeVerbs: false -``` - -Metrics generated: -``` -dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders",status="200"} 1 -dapr_http_server_request_count{app_id="order-service",method="POST",path="/orders",status="200"} 1 -``` - -In this example, the HTTP method is included in the metrics, resulting in a separate metric for each request to the `/orders` endpoint. - -#### Exclude HTTP verbs - -Configuration: -```yaml -http: - excludeVerbs: true -``` - -Metrics generated: -``` -dapr_http_server_request_count{app_id="order-service",method="",path="/orders",status="200"} 2 -``` - -In this example, the HTTP method is excluded from the metrics, resulting in a single metric for all requests to the `/orders` endpoint. +Dapr 1.13 introduces a new option for the Dapr Configuration resource `spec.metrics.http.increasedCardinality`: when set to `false`, it reports metrics for the HTTP server for each "abstract" method (for example, requesting from a state store) instead of creating a "bucket" for each concrete request path. +The default value of `spec.metrics.http.increasedCardinality` is `true` in Dapr 1.13, to maintain the same behavior as Dapr 1.12 and older. However, the value will change to `false` (low-cardinality metrics by default) in Dapr 1.14. +Setting `spec.metrics.http.increasedCardinality` to `false` is **recommended** to all Dapr users, to reduce resource consumption. The pre-1.13 behavior, which is used when the option is `true`, is considered legacy and is only maintained for users who have special requirements around backwards-compatibility. ## Transform metrics with regular expressions diff --git a/daprdocs/content/en/reference/resource-specs/configuration-schema.md b/daprdocs/content/en/reference/resource-specs/configuration-schema.md index 3507975de..746d4b5c3 100644 --- a/daprdocs/content/en/reference/resource-specs/configuration-schema.md +++ b/daprdocs/content/en/reference/resource-specs/configuration-schema.md @@ -38,10 +38,6 @@ spec: regex: {} http: increasedCardinality: - pathMatching: - - - - - excludeVerbs: httpPipeline: # for incoming http calls handlers: - name: From 868493549a0c90252fe875c20479175a6898091e Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 8 Jul 2024 17:02:58 -0400 Subject: [PATCH 13/92] updates and add website root Signed-off-by: Hannah Hunter --- .github/workflows/website-root.yml | 109 ++++++++++++++++++ README.md | 4 +- daprdocs/config.toml | 9 +- .../support/support-release-policy.md | 2 + .../shortcodes/dapr-latest-version.html | 2 +- 5 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/website-root.yml diff --git a/.github/workflows/website-root.yml b/.github/workflows/website-root.yml new file mode 100644 index 000000000..aa2b3fe0a --- /dev/null +++ b/.github/workflows/website-root.yml @@ -0,0 +1,109 @@ +name: Azure Static Web App Root + +on: + workflow_dispatch: + push: + branches: + - v1.14 + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - v1.14 + +concurrency: + # Cancel the previously triggered build for only PR build. + group: website-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +jobs: + build_and_deploy_job: + name: Build Hugo Website + if: github.event.action != 'closed' + runs-on: ubuntu-latest + env: + SWA_BASE: 'proud-bay-0e9e0e81e' + HUGO_ENV: production + steps: + - name: Checkout docs repo + uses: actions/checkout@v3 + with: + submodules: true + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '14' + - name: Setup Hugo + uses: peaceiris/actions-hugo@v2.5.0 + with: + hugo-version: 0.102.3 + extended: true + - name: Setup Docsy + run: | + cd daprdocs + git submodule update --init --recursive + sudo npm install -D --save autoprefixer + sudo npm install -D --save postcss-cli + - name: Build Hugo Website + run: | + cd daprdocs + git config --global --add safe.directory /github/workspace + if [ $GITHUB_EVENT_NAME == 'pull_request' ]; then + STAGING_URL="https://${SWA_BASE}-${{github.event.number}}.westus2.azurestaticapps.net/" + fi + hugo ${STAGING_URL+-b "$STAGING_URL"} + - name: Deploy docs site + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_PROUD_BAY_0E9E0E81E }} + repo_token: ${{ secrets.GITHUB_TOKEN }} + action: "upload" + app_location: "daprdocs/public/" + api_location: "daprdocs/public/" + output_location: "" + skip_app_build: true + skip_deploy_on_missing_secrets: true + - name: Upload Hugo artifacts + uses: actions/upload-artifact@v3 + with: + name: hugo_build + path: ./daprdocs/public/ + if-no-files-found: error + + close_staging_site: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Close Pull Request Job + steps: + - name: Close Pull Request + id: closepullrequest + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_PROUD_BAY_0E9E0E81E }} + action: "close" + skip_deploy_on_missing_secrets: true + + algolia_index: + name: Index site for Algolia + if: github.event_name == 'push' + needs: ['build_and_deploy_job'] + runs-on: ubuntu-latest + env: + ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} + ALGOLIA_API_WRITE_KEY: ${{ secrets.ALGOLIA_API_WRITE_KEY }} + ALGOLIA_INDEX_NAME: daprdocs + steps: + - name: Checkout docs repo + uses: actions/checkout@v2 + with: + submodules: false + - name: Download Hugo artifacts + uses: actions/download-artifact@v3 + with: + name: hugo_build + path: site/ + - name: Install Python packages + run: | + pip install --upgrade bs4 + pip install --upgrade 'algoliasearch>=2.0,<3.0' + - name: Index site + run: python ./.github/scripts/algolia.py ./site \ No newline at end of file diff --git a/README.md b/README.md index c8a4a8ada..dd2ccfe51 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ The following branches are currently maintained: | Branch | Website | Description | | ------------------------------------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------ | -| [v1.13](https://github.com/dapr/docs) (primary) | https://docs.dapr.io | Latest Dapr release documentation. Typo fixes, clarifications, and most documentation goes here. | -| [v1.14](https://github.com/dapr/docs/tree/v1.14) (pre-release) | https://v1-14.docs.dapr.io/ | Pre-release documentation. Doc updates that are only applicable to v1.14+ go here. | +| [v1.14](https://github.com/dapr/docs) (primary) | https://docs.dapr.io | Latest Dapr release documentation. Typo fixes, clarifications, and most documentation goes here. | +| [v1.15](https://github.com/dapr/docs/tree/v1.15) (pre-release) | https://v1-15.docs.dapr.io/ | Pre-release documentation. Doc updates that are only applicable to v1.15+ go here. | For more information visit the [Dapr branch structure](https://docs.dapr.io/contributing/docs-contrib/contributing-docs/#branch-guidance) document. diff --git a/daprdocs/config.toml b/daprdocs/config.toml index 4ea4fba47..e30441758 100644 --- a/daprdocs/config.toml +++ b/daprdocs/config.toml @@ -1,5 +1,5 @@ # Site Configuration -baseURL = "https://v1-14.docs.dapr.io" +baseURL = "https://docs.dapr.io" title = "Dapr Docs" theme = "docsy" disableFastRender = true @@ -205,11 +205,14 @@ archived_version = false url_latest_version = "https://docs.dapr.io" [[params.versions]] - version = "v1.14 (preview)" + version = "v1.15 (preview)" url = "#" [[params.versions]] - version = "v1.13 (latest)" + version = "v1.14 (latest)" url = "https://docs.dapr.io" +[[params.versions]] + version = "v1.13" + url = "https://v1-13.docs.dapr.io" [[params.versions]] version = "v1.12" url = "https://v1-12.docs.dapr.io" diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index d32d4b52a..5cc75ce95 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,6 +45,7 @@ The table below shows the versions of Dapr releases that have been tested togeth | Release date | Runtime | CLI | SDKs | Dashboard | Status | Release notes | |--------------------|:--------:|:--------|---------|---------|---------|------------| +| July 24th 2024 | 1.14.0
| 1.14.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | | May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | | May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | | April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | @@ -140,6 +141,7 @@ General guidance on upgrading can be found for [self hosted mode]({{< ref self-h | 1.13.0 | N/A | 1.13.2 | | 1.13.0 | N/A | 1.13.3 | | 1.13.0 | N/A | 1.13.4 | +| 1.14.0 | N/A | 1.14.0 | ## Upgrade on Hosting platforms diff --git a/daprdocs/layouts/shortcodes/dapr-latest-version.html b/daprdocs/layouts/shortcodes/dapr-latest-version.html index 68a8266ae..c64a87827 100644 --- a/daprdocs/layouts/shortcodes/dapr-latest-version.html +++ b/daprdocs/layouts/shortcodes/dapr-latest-version.html @@ -1 +1 @@ -{{- if .Get "short" }}1.13{{ else if .Get "long" }}1.13.4{{ else if .Get "cli" }}1.13.0{{ else }}1.13.4{{ end -}} +{{- if .Get "short" }}1.14{{ else if .Get "long" }}1.14.0{{ else if .Get "cli" }}1.14.0{{ else }}1.14.0{{ end -}} From a2ba9ab5af041e4f34bf0f34fdd6344a8fb93be2 Mon Sep 17 00:00:00 2001 From: salaboy Date: Tue, 9 Jul 2024 09:25:50 +0100 Subject: [PATCH 14/92] upgrade pre-req to JDK 17 Signed-off-by: salaboy --- .../en/getting-started/quickstarts/bindings-quickstart.md | 2 +- .../quickstarts/configuration-quickstart.md | 2 +- .../en/getting-started/quickstarts/pubsub-quickstart.md | 2 +- .../resiliency/resiliency-serviceinvo-quickstart.md | 2 +- .../quickstarts/resiliency/resiliency-state-quickstart.md | 2 +- .../en/getting-started/quickstarts/secrets-quickstart.md | 2 +- .../quickstarts/serviceinvocation-quickstart.md | 4 ++-- .../quickstarts/statemanagement-quickstart.md | 4 ++-- .../en/getting-started/quickstarts/workflow-quickstart.md | 8 ++++---- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md index 4e40a5159..4597228b7 100644 --- a/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md @@ -651,7 +651,7 @@ In the YAML file: For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md index a9c563ba1..368a1bd5c 100644 --- a/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md @@ -389,7 +389,7 @@ try For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md index 82d588442..3b232f995 100644 --- a/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md @@ -512,7 +512,7 @@ Console.WriteLine("Published data: " + order); For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md index 92f926107..9e5fe6983 100644 --- a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md @@ -693,7 +693,7 @@ dapr run --app-port 7001 --app-id order-processor --app-protocol http --dapr-htt For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md index 8a35d2442..b47ed6237 100644 --- a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md @@ -533,7 +533,7 @@ INFO[0036] Recovered processing operation component[statestore] output. For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md index de12598d2..9d1187e97 100644 --- a/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md @@ -356,7 +356,7 @@ Order-processor output: For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md index 0164e35fd..ada18093d 100644 --- a/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md @@ -458,7 +458,7 @@ var response = await client.PostAsync($"{baseURL}/orders", content); For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. @@ -1156,7 +1156,7 @@ Dapr invokes an application on any Dapr instance. In the code, the sidecar progr For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md index a0448f0d3..95357c4eb 100644 --- a/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md @@ -419,7 +419,7 @@ In the YAML file: For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. @@ -1050,7 +1050,7 @@ In the YAML file: For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. diff --git a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md index 8e1adb0c5..8f5a887f9 100644 --- a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md @@ -746,10 +746,10 @@ The `order-processor` console app starts and manages the lifecycle of an order p For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): - - [Microsoft JDK 11](https://docs.microsoft.com/java/openjdk/download#openjdk-11) - - [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) - - [OpenJDK 11](https://jdk.java.net/11/) +- Java JDK 17 (or greater): + - [Microsoft JDK 17](https://docs.microsoft.com/java/openjdk/download#openjdk-17) + - [Oracle JDK 17](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK17) + - [OpenJDK 17](https://jdk.java.net/17/) - [Apache Maven](https://maven.apache.org/install.html) version 3.x. - [Docker Desktop](https://www.docker.com/products/docker-desktop) From a6dca4c74379817ac3fecedc3ddf37413436fd5a Mon Sep 17 00:00:00 2001 From: salaboy Date: Tue, 9 Jul 2024 09:42:35 +0100 Subject: [PATCH 15/92] adding pubsub Signed-off-by: salaboy --- .../content/en/getting-started/quickstarts/pubsub-quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md index 3b232f995..abb1293fa 100644 --- a/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md @@ -1321,7 +1321,7 @@ In the YAML file: For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): +- Java JDK 17 (or greater): - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - OpenJDK - [Apache Maven](https://maven.apache.org/install.html), version 3.x. From 2d841960da63a01ca2a0e428031bd8f429ce5f33 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 9 Jul 2024 12:12:09 -0400 Subject: [PATCH 16/92] update latest to 1.13.5 Signed-off-by: Hannah Hunter --- .../content/en/operations/support/support-release-policy.md | 5 ++--- daprdocs/layouts/shortcodes/dapr-latest-version.html | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index 1ad4e2b15..735c1ca1d 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,6 +45,7 @@ The table below shows the versions of Dapr releases that have been tested togeth | Release date | Runtime | CLI | SDKs | Dashboard | Status | Release notes | |--------------------|:--------:|:--------|---------|---------|---------|------------| +| June 28th 2024 | 1.13.5
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.5 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.5) | | May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | | May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | | April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | @@ -137,9 +138,7 @@ General guidance on upgrading can be found for [self hosted mode]({{< ref self-h | 1.10.0 | N/A | 1.10.8 | | 1.11.0 | N/A | 1.11.4 | | 1.12.0 | N/A | 1.12.4 | -| 1.13.0 | N/A | 1.13.2 | -| 1.13.0 | N/A | 1.13.3 | -| 1.13.0 | N/A | 1.13.4 | +| 1.13.0 | N/A | 1.13.5 | ## Upgrade on Hosting platforms diff --git a/daprdocs/layouts/shortcodes/dapr-latest-version.html b/daprdocs/layouts/shortcodes/dapr-latest-version.html index 68a8266ae..4479012ea 100644 --- a/daprdocs/layouts/shortcodes/dapr-latest-version.html +++ b/daprdocs/layouts/shortcodes/dapr-latest-version.html @@ -1 +1 @@ -{{- if .Get "short" }}1.13{{ else if .Get "long" }}1.13.4{{ else if .Get "cli" }}1.13.0{{ else }}1.13.4{{ end -}} +{{- if .Get "short" }}1.13{{ else if .Get "long" }}1.13.5{{ else if .Get "cli" }}1.13.0{{ else }}1.13.5{{ end -}} From 867b15348db637c71e2ae5d3447a49aa5cd22a73 Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Tue, 9 Jul 2024 17:57:37 -0400 Subject: [PATCH 17/92] add python examples to crypto how-to (#4255) ` Signed-off-by: Hannah Hunter --- .../cryptography/howto-cryptography.md | 143 +++++++++++++----- 1 file changed, 104 insertions(+), 39 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/cryptography/howto-cryptography.md b/daprdocs/content/en/developing-applications/building-blocks/cryptography/howto-cryptography.md index ca9d02796..37923e06c 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/cryptography/howto-cryptography.md +++ b/daprdocs/content/en/developing-applications/building-blocks/cryptography/howto-cryptography.md @@ -15,7 +15,39 @@ Dapr cryptography is currently in alpha. ## Encrypt -{{< tabs "JavaScript" "Go" ".NET" >}} +{{< tabs "Python" "JavaScript" ".NET" "Go" >}} + +{{% codetab %}} + + + +Using the Dapr SDK in your project, with the gRPC APIs, you can encrypt a stream of data, such as a file or a string: + +```python +# When passing data (a buffer or string), `encrypt` returns a Buffer with the encrypted message +def encrypt_decrypt_string(dapr: DaprClient): + message = 'The secret is "passw0rd"' + + # Encrypt the message + resp = dapr.encrypt( + data=message.encode(), + options=EncryptOptions( + # Name of the cryptography component (required) + component_name=CRYPTO_COMPONENT_NAME, + # Key stored in the cryptography component (required) + key_name=RSA_KEY_NAME, + # Algorithm used for wrapping the key, which must be supported by the key named above. + # Options include: "RSA", "AES" + key_wrap_algorithm='RSA', + ), + ) + + # The method returns a readable stream, which we read in full in memory + encrypt_bytes = resp.read() + print(f'Encrypted the message, got {len(encrypt_bytes)} bytes') +``` + +{{% /codetab %}} {{% codetab %}} @@ -59,6 +91,26 @@ await pipeline( {{% codetab %}} + +Using the Dapr SDK in your project, with the gRPC APIs, you can encrypt data in a string or a byte array: + +```csharp +using var client = new DaprClientBuilder().Build(); + +const string componentName = "azurekeyvault"; //Change this to match your cryptography component +const string keyName = "myKey"; //Change this to match the name of the key in your cryptographic store + +const string plainText = "This is the value we're going to encrypt today"; + +//Encode the string to a UTF-8 byte array and encrypt it +var plainTextBytes = Encoding.UTF8.GetBytes(plainText); +var encryptedBytesResult = await client.EncryptAsync(componentName, plaintextBytes, keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa)); +``` + +{{% /codetab %}} + +{{% codetab %}} + Using the Dapr SDK in your project, you can encrypt a stream of data, such as a file. @@ -136,32 +188,45 @@ if err != nil { {{% /codetab %}} -{{% codetab %}} - - -Using the Dapr SDK in your project, with the gRPC APIs, you can encrypt data in a string or a byte array: - -```csharp -using var client = new DaprClientBuilder().Build(); - -const string componentName = "azurekeyvault"; //Change this to match your cryptography component -const string keyName = "myKey"; //Change this to match the name of the key in your cryptographic store - -const string plainText = "This is the value we're going to encrypt today"; - -//Encode the string to a UTF-8 byte array and encrypt it -var plainTextBytes = Encoding.UTF8.GetBytes(plainText); -var encryptedBytesResult = await client.EncryptAsync(componentName, plaintextBytes, keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa)); -``` - -{{% /codetab %}} - {{< /tabs >}} ## Decrypt -{{< tabs "JavaScript" "Go" ".NET" >}} +{{< tabs "Python" "JavaScript" ".NET" "Go" >}} + +{{% codetab %}} + + + +To decrypt a stream of data, use `decrypt`. + +```python +def encrypt_decrypt_string(dapr: DaprClient): + message = 'The secret is "passw0rd"' + + # ... + + # Decrypt the encrypted data + resp = dapr.decrypt( + data=encrypt_bytes, + options=DecryptOptions( + # Name of the cryptography component (required) + component_name=CRYPTO_COMPONENT_NAME, + # Key stored in the cryptography component (required) + key_name=RSA_KEY_NAME, + ), + ) + + # The method returns a readable stream, which we read in full in memory + decrypt_bytes = resp.read() + print(f'Decrypted the message, got {len(decrypt_bytes)} bytes') + + print(decrypt_bytes.decode()) + assert message == decrypt_bytes.decode() +``` + +{{% /codetab %}} {{% codetab %}} @@ -191,23 +256,6 @@ await pipeline( {{% codetab %}} - - -To decrypt a file, use the `Decrypt` gRPC API to your project. - -In the following example, `out` is a stream that can be written to file or read in memory, as in the examples above. - -```go -out, err := sdkClient.Decrypt(context.Background(), rf, dapr.EncryptOptions{ - // Only required option is the component name - ComponentName: "mycryptocomponent", -}) -``` - -{{% /codetab %}} - -{{% codetab %}} - To decrypt a string, use the 'DecryptAsync' gRPC API in your project. @@ -229,6 +277,23 @@ public async Task DecryptBytesAsync(byte[] encryptedBytes) {{% /codetab %}} +{{% codetab %}} + + + +To decrypt a file, use the `Decrypt` gRPC API to your project. + +In the following example, `out` is a stream that can be written to file or read in memory, as in the examples above. + +```go +out, err := sdkClient.Decrypt(context.Background(), rf, dapr.EncryptOptions{ + // Only required option is the component name + ComponentName: "mycryptocomponent", +}) +``` + +{{% /codetab %}} + {{< /tabs >}} ## Next steps From 64a22cbe3c8414d09f36fd3900d11603657b6ce5 Mon Sep 17 00:00:00 2001 From: Filinto Duran Date: Wed, 10 Jul 2024 09:36:58 -0500 Subject: [PATCH 18/92] add info about custom latency buckets (#4236) * doc: http metrics path normalization Signed-off-by: nelson.parente * doc: code review & path matching rename Signed-off-by: nelson.parente * doc: add configuration examples Signed-off-by: nelson.parente * update: update docs based on last proposal changes Signed-off-by: nelson.parente * feat: more updates based on the ingress/egress merge Signed-off-by: nelson.parente * doc: code review comments Signed-off-by: nelson.parente * doc: code review comments Signed-off-by: nelson.parente * feat: add excludeVerbs Signed-off-by: nelson.parente * feat: new line Signed-off-by: nelson.parente * feat: add review meeting changes Signed-off-by: nelson.parente * v1.14 - cherry pick path normalization Signed-off-by: Filinto Duran * add additional changes Signed-off-by: Filinto Duran * add additional changes Signed-off-by: Filinto Duran * add additional changes Signed-off-by: Filinto Duran * format table Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Mark Fussell Signed-off-by: Filinto Duran * explain buckets Signed-off-by: Filinto Duran * Apply suggestions from code review Co-authored-by: Alice Gibbons Signed-off-by: Mark Fussell * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Mark Fussell Signed-off-by: Filinto Duran * Update daprdocs/content/en/operations/observability/metrics/metrics-overview.md Co-authored-by: Mark Fussell Signed-off-by: Filinto Duran --------- Signed-off-by: nelson.parente Signed-off-by: Filinto Duran Signed-off-by: Filinto Duran Signed-off-by: Mark Fussell Signed-off-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Co-authored-by: nelson.parente Co-authored-by: Mark Fussell Co-authored-by: Alice Gibbons Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> --- .../configuration/configuration-overview.md | 18 ++++--- .../observability/metrics/metrics-overview.md | 51 +++++++++++++++++++ .../resource-specs/configuration-schema.md | 3 ++ 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/daprdocs/content/en/operations/configuration/configuration-overview.md b/daprdocs/content/en/operations/configuration/configuration-overview.md index 5b6a4d73d..0cba2414c 100644 --- a/daprdocs/content/en/operations/configuration/configuration-overview.md +++ b/daprdocs/content/en/operations/configuration/configuration-overview.md @@ -108,6 +108,7 @@ The `metrics` section under the `Configuration` spec contains the following prop metrics: enabled: true rules: [] + latencyDistributionBuckets: [] http: increasedCardinality: true pathMatching: @@ -121,17 +122,18 @@ metrics: excludeVerbs: false ``` -In the examples above, the path filter `/orders/{orderID}/items/{itemID}` would return a single metric count matching all the `orderIDs` and all the `itemIDs`, rather than multiple metrics for each `itemID`. For more information, see [HTTP metrics path matching]({{< ref "metrics-overview.md#http-metrics-path-matching" >}}). +In the examples above this path filter `/orders/{orderID}/items/{itemID}` would return a single metric count matching all the orderIDs and all the itemIDs rather than multiple metrics for each itemID. For more information see [HTTP metrics path matching]({{< ref "metrics-overview.md#http-metrics-path-matching" >}}) The following table lists the properties for metrics: -| Property | Type | Description | -|--------------|--------|-------------| -| `enabled` | boolean | When set to true, the default, enables metrics collection and the metrics endpoint. | -| `rules` | array | Named rule to filter metrics. Each rule contains a set of `labels` to filter on and a `regex` expression to apply to the metrics path. | -| `http.increasedCardinality` | boolean | When set to `true` (default), in the Dapr HTTP server, each request path causes the creation of a new "bucket" of metrics. This can cause issues, including excessive memory consumption when there many different requested endpoints (such as when interacting with RESTful APIs).
To mitigate high memory usage and egress costs associated with [high cardinality metrics]({{< ref "metrics-overview.md#high-cardinality-metrics" >}}) with the HTTP server, you should set the `metrics.http.increasedCardinality` property to `false`.| -| `http.pathMatching` | array | Paths used for path matching, allowing users to define matching paths in order to manage cardinality. | -| `http.excludeVerbs` | boolean | When set to `true` (default is `false`), the Dapr HTTP server ignores each request HTTP verb when building the method metric label. | +| Property | Type | Description | +|------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `enabled` | boolean | When set to true, the default, enables metrics collection and the metrics endpoint. | +| `rules` | array | Named rule to filter metrics. Each rule contains a set of `labels` to filter on and a `regex` expression to apply to the metrics path. | +| `latencyDistributionBuckets` | array | Array of latency distribution buckets in milliseconds for latency metrics histograms. | +| `http.increasedCardinality` | boolean | When set to `true` (default), in the Dapr HTTP server each request path causes the creation of a new "bucket" of metrics. This can cause issues, including excessive memory consumption, when there many different requested endpoints (such as when interacting with RESTful APIs).
To mitigate high memory usage and egress costs associated with [high cardinality metrics]({{< ref "metrics-overview.md#high-cardinality-metrics" >}}) with the HTTP server, you should set the `metrics.http.increasedCardinality` property to `false`. | +| `http.pathMatching` | array | Array of paths for path matching, allowing users to define matching paths to manage cardinality. | +| `http.excludeVerbs` | boolean | When set to true (default is false), the Dapr HTTP server ignores each request HTTP verb when building the method metric label. | To further help managing cardinality, path matching allows specified paths matched according to defined patterns, reducing the number of unique metrics paths and thus controlling metric cardinality. This feature is particularly useful for applications with dynamic URLs, ensuring that metrics remain meaningful and manageable without excessive memory consumption. diff --git a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md index da2ec6fc3..be0d9bfcd 100644 --- a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md +++ b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md @@ -198,7 +198,58 @@ dapr_http_server_request_count{app_id="order-service",method="",path="/orders",s In this example, the HTTP method is excluded from the metrics, resulting in a single metric for all requests to the `/orders` endpoint. +## Configuring custom latency histogram buckets +Dapr uses cumulative histogram metrics to group latency values into buckets, where each bucket contains: +- A count of the number of requests with that latency +- All the requests with lower latency + +### Using the default latency bucket configurations + +By default, Dapr groups request latency metrics into the following buckets: + +``` +1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000 +``` + +Grouping latency values in a cumulative fashion allows buckets to be used or dropped as needed for increased or decreased granularity of data. +For example, if a request takes 3ms, it's counted in the 3ms bucket, the 4ms bucket, the 5ms bucket, and so on. +Similarly, if a request takes 10ms, it's counted in the 10ms bucket, the 13ms bucket, the 16ms bucket, and so on. +After these two requests have completed, the 3ms bucket has a count of 1 and the 10ms bucket has a count of 2, since both the 3ms and 10ms requests are included here. + +This shows up as follows: + +|1|2|3|4|5|6|8|10|13|16|20|25|30|40|50|65|80|100|130|160| ..... | 100000 | +|-|-|-|-|-|-|-|--|--|--|--|--|--|--|--|--|--|---|---|---|-------|--------| +|0|0|1|1|1|1|1| 2| 2| 2| 2| 2| 2| 2| 2| 2| 2| 2 | 2 | 2 | ..... | 2 | + + +The default number of buckets works well for most use cases, but can be adjusted as needed. Each request creates 34 different metrics, leaving this value to grow considerably for a large number of applications. +More accurate latency percentiles can be achieved by increasing the number of buckets. However, a higher number of buckets increases the amount of memory used to store the metrics, potentially negatively impacting your monitoring system. + +It is recommended to keep the number of latency buckets set to the default value, unless you are seeing unwanted memory pressure in your monitoring system. Configuring the number of buckets allows you to choose applications where: +- You want to see more detail with a higher number of buckets +- Broader values are sufficient by reducing the buckets + +Take note of the default latency values your applications are producing before configuring the number buckets. +### Customizing latency buckets to your scenario + +Tailor the latency buckets to your needs, by modifying the `spec.metrics.latencyDistributionBuckets` field in the [Dapr configuration spec]({{< ref configuration-schema.md >}}) for your application(s). + +For example, if you aren't interested in extremely low latency values (1-10ms), you can group them in a single 10ms bucket. Similarly, you can group the high values in a single bucket (1000-5000ms), while keeping more detail in the middle range of values that you are most interested in. + +The following Configuration spec example replaces the default 34 buckets with 11 buckets, giving a higher level of granularity in the middle range of values: + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: custom-metrics +spec: + metrics: + enabled: true + latencyDistributionBuckets: [10, 25, 40, 50, 70, 100, 150, 200, 500, 1000, 5000] +``` ## Transform metrics with regular expressions diff --git a/daprdocs/content/en/reference/resource-specs/configuration-schema.md b/daprdocs/content/en/reference/resource-specs/configuration-schema.md index 3507975de..b52228c16 100644 --- a/daprdocs/content/en/reference/resource-specs/configuration-schema.md +++ b/daprdocs/content/en/reference/resource-specs/configuration-schema.md @@ -36,6 +36,9 @@ spec: labels: - name: regex: {} + latencyDistributionBuckets: + - + - http: increasedCardinality: pathMatching: From 5c474ddcdcd30045ef7152c0b5bf04e0a0fd2737 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 10 Jul 2024 11:52:35 -0400 Subject: [PATCH 19/92] add section for overriding db saved message Signed-off-by: Hannah Hunter --- .../state-management/howto-outbox.md | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index 59dcf3c37..9f995eaed 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -34,7 +34,7 @@ The outbox feature can be used with using any [transactional state store]({{< re Message brokers that work with the competing consumer pattern (for example, [Apache Kafka]({{< ref setup-apache-kafka>}})) are encouraged to reduce the chances of duplicate events. {{% /alert %}} -## Usage +## Enable the outbox pattern To enable the outbox feature, add the following required and optional fields on a state store component: @@ -68,6 +68,8 @@ spec: | outboxPubsub | No | `outboxPublishPubsub` | Sets the pub/sub component used by Dapr to coordinate the state and pub/sub transactions. If not set, the pub/sub component configured with `outboxPublishPubsub` is used. This is useful if you want to separate the pub/sub component used to send the notification state changes from the one used to coordinate the transaction | outboxDiscardWhenMissingState | No | `false` | By setting `outboxDiscardWhenMissingState` to `true`, Dapr discards the transaction if it cannot find the state in the database and does not retry. This setting can be useful if the state store data has been deleted for any reason before Dapr was able to deliver the message and you would like Dapr to drop the items from the pub/sub and stop retrying to fetch the state +## Additional configurations + ### Combining outbox and non-outbox messages on the same state store If you want to use the same state store for sending both outbox and non-outbox messages, simply define two state store components that connect to the same state store, where one has the outbox feature and the other does not. @@ -106,6 +108,40 @@ spec: value: "newOrder" ``` +### Shape the outbox pattern message + +You can override the outbox pattern message saved to the database during the transaction by setting a different message. This is done via a projected transaction payload, which is ignored in the database, but used as the outbox pattern message published to the user topic. + +In the following Go example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. + +```go +_, err = runtimev1pb.NewDaprClient(conn).ExecuteStateTransaction(ctx, &runtimev1pb.ExecuteStateTransactionRequest{ + StoreName: "mystore", + Operations: []*runtimev1pb.TransactionalStateOperation{ + { + OperationType: "upsert", + Request: &common.StateItem{ + Key: "1", + Value: []byte("2"), + }, + }, + { + OperationType: "upsert", + Request: &common.StateItem{ + Key: "1", + Value: []byte("3"), + // Override the data payload saved to the database + Metadata: map[string]string{ + "outbox.projection": "true", + }, + }, + }, + }, + }) +``` + +By setting the metadata item `"outbox.projection"` to `"true"`, the transaction value saved to the database is ignored, while the second value is published to the configured pub/sub topic. + ## Demo Watch [this video for an overview of the outbox pattern](https://youtu.be/rTovKpG0rhY?t=1338): From 582a0ad0d52e773b73a43a02b9187ec0999ae26a Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:43:43 -0400 Subject: [PATCH 20/92] Update readme.md Signed-off-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> --- .github/iac/swa/readme.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/iac/swa/readme.md b/.github/iac/swa/readme.md index dd5687bf2..81bfcedf3 100644 --- a/.github/iac/swa/readme.md +++ b/.github/iac/swa/readme.md @@ -34,16 +34,17 @@ This assumes you have an existing [user-assigned managed identity](https://learn 2) Deploy using the Azure Dev CLI -The first time, and any updates to this environment - -```bash -azd up -``` - -For subsequent environments/sites, create a side-by-side environment like this: +Start by creating a create a side-by-side azd environment: ```bash azd env new +``` + +For example, you can name the new environment something like: `dapr-docs-v1-15`. + +Now, deploy the Dapr docs SWA in the new azd environment using the following command: + +```bash azd up ``` From 1841418449f14af0cc7531d0e841e2dd072b2b23 Mon Sep 17 00:00:00 2001 From: Arthur Poiret Date: Thu, 11 Jul 2024 22:23:45 +0000 Subject: [PATCH 21/92] add rabbitmq single active consumer parameter Signed-off-by: Arthur Poiret --- .../supported-pubsub/setup-rabbitmq.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md index 34c285037..1bb9eb75f 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md @@ -451,6 +451,28 @@ You can set a time-to-live (TTL) value at either the message or component level. If you set both component-level and message-level TTL, the default component-level TTL is ignored in favor of the message-level TTL. {{% /alert %}} +## Single Active Consumer + +Enable RabbitMQ [Single Active Consumer](https://www.rabbitmq.com/docs/consumers#single-active-consumer) parameter on queues. + +```yml +apiVersion: dapr.io/v2alpha1 +kind: Subscription +metadata: + name: pubsub +spec: + topic: orders + routes: + default: /orders + pubsubname: order-pub-sub + metadata: + singleActiveConsumer: "true" +``` + +{{% alert title="Note" color="primary" %}} +The Dapr runtime acts as the single active consumer from RabbitMQ's perspective. To allow another application instance to take over in case of failure, Dapr must [probe the application's health]({{< ref "app-health.md" >}}) and unsubscribe from pub/sub component. +{{% /alert %}} + ## Related links - [Basic schema for a Dapr component]({{< ref component-schema >}}) in the Related links section From eb69cd916ecd5697751ec140f1e852e93aefb4fd Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Fri, 12 Jul 2024 15:10:29 -0400 Subject: [PATCH 22/92] update example based on go sdk for transactional state Signed-off-by: Hannah Hunter --- .../state-management/howto-outbox.md | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index 9f995eaed..7b3e25b1c 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -115,29 +115,29 @@ You can override the outbox pattern message saved to the database during the tra In the following Go example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. ```go -_, err = runtimev1pb.NewDaprClient(conn).ExecuteStateTransaction(ctx, &runtimev1pb.ExecuteStateTransactionRequest{ - StoreName: "mystore", - Operations: []*runtimev1pb.TransactionalStateOperation{ - { - OperationType: "upsert", - Request: &common.StateItem{ - Key: "1", - Value: []byte("2"), - }, - }, - { - OperationType: "upsert", - Request: &common.StateItem{ - Key: "1", - Value: []byte("3"), - // Override the data payload saved to the database - Metadata: map[string]string{ - "outbox.projection": "true", - }, - }, - }, - }, - }) +ops := make([]*dapr.StateOperation, 0) + +op1 := &dapr.StateOperation{ + Type: dapr.StateOperationTypeUpsert, + Item: &dapr.SetStateItem{ + Key: "key1", + Value: []byte("2"), + }, +} +op2 := &dapr.StateOperation{ + Type: dapr.StateOperationTypeUpsert, + Item: &dapr.SetStateItem{ + Key: "key1", + Value: []byte("3"), + // Override the data payload saved to the database + Metadata: map[string]string{ + "outbox.projection": "true", + }, + }, +} +ops = append(ops, op1, op2) +meta := map[string]string{} +err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) ``` By setting the metadata item `"outbox.projection"` to `"true"`, the transaction value saved to the database is ignored, while the second value is published to the configured pub/sub topic. From 50239157d257212dc3d777aab2a01899b5408c81 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 15 Jul 2024 11:32:02 -0400 Subject: [PATCH 23/92] update config.toml for v1.15 Signed-off-by: Hannah Hunter --- daprdocs/config.toml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/daprdocs/config.toml b/daprdocs/config.toml index 4ea4fba47..ad0364636 100644 --- a/daprdocs/config.toml +++ b/daprdocs/config.toml @@ -1,5 +1,5 @@ # Site Configuration -baseURL = "https://v1-14.docs.dapr.io" +baseURL = "https://v1-15.docs.dapr.io" title = "Dapr Docs" theme = "docsy" disableFastRender = true @@ -196,20 +196,23 @@ offlineSearch = false github_repo = "https://github.com/dapr/docs" github_project_repo = "https://github.com/dapr/dapr" github_subdir = "daprdocs" -github_branch = "v1.14" +github_branch = "v1.15" # Versioning -version_menu = "v1.14 (preview)" -version = "v1.14" +version_menu = "v1.15 (preview)" +version = "v1.15" archived_version = false url_latest_version = "https://docs.dapr.io" [[params.versions]] - version = "v1.14 (preview)" + version = "v1.15 (preview)" url = "#" [[params.versions]] - version = "v1.13 (latest)" + version = "v1.14 (latest)" url = "https://docs.dapr.io" +[[params.versions]] + version = "v1.13" + url = "https://v1-13.docs.dapr.io" [[params.versions]] version = "v1.12" url = "https://v1-12.docs.dapr.io" From b5c0754d9aeeb68ce7e45cb51176c816643e8ec2 Mon Sep 17 00:00:00 2001 From: ibandhiya Date: Tue, 16 Jul 2024 19:55:17 +0530 Subject: [PATCH 24/92] Update service-mesh.md (#4261) ref link added for distributed tracing Signed-off-by: ibandhiya --- daprdocs/content/en/concepts/service-mesh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/concepts/service-mesh.md b/daprdocs/content/en/concepts/service-mesh.md index 0e3c4cf9e..d0f686dc3 100644 --- a/daprdocs/content/en/concepts/service-mesh.md +++ b/daprdocs/content/en/concepts/service-mesh.md @@ -7,7 +7,7 @@ description: > How Dapr compares to and works with service meshes --- -Dapr uses a sidecar architecture, running as a separate process alongside the application and includes features such as service invocation, network security, and distributed tracing. This often raises the question: how does Dapr compare to service mesh solutions such as [Linkerd](https://linkerd.io/), [Istio](https://istio.io/) and [Open Service Mesh](https://openservicemesh.io/) among others? +Dapr uses a sidecar architecture, running as a separate process alongside the application and includes features such as service invocation, network security, and [distributed tracing](https://middleware.io/blog/what-is-distributed-tracing/). This often raises the question: how does Dapr compare to service mesh solutions such as [Linkerd](https://linkerd.io/), [Istio](https://istio.io/) and [Open Service Mesh](https://openservicemesh.io/) among others? ## How Dapr and service meshes compare While Dapr and service meshes do offer some overlapping capabilities, **Dapr is not a service mesh**, where a service mesh is defined as a *networking* service mesh. Unlike a service mesh which is focused on networking concerns, Dapr is focused on providing building blocks that make it easier for developers to build applications as microservices. Dapr is developer-centric, versus service meshes which are infrastructure-centric. From 8b105e9949538391c9065f53a2d77ef31129c9ef Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 16 Jul 2024 10:59:07 -0400 Subject: [PATCH 25/92] add section for overriding cloudevent fields Signed-off-by: Hannah Hunter --- .../state-management/howto-outbox.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index 7b3e25b1c..64aca9960 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -142,6 +142,39 @@ err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) By setting the metadata item `"outbox.projection"` to `"true"`, the transaction value saved to the database is ignored, while the second value is published to the configured pub/sub topic. +### Override Dapr-generated cloudevent fields + +You can also override the [Dapr-generated cloudevent fields]({{< ref "pubsub-cloudevents.md#dapr-generated-cloudevents-example" >}}) on the published outbox event with custom cloudevent metadata. + +```go +ops := make([]*dapr.StateOperation, 0) + +op1 := &dapr.StateOperation{ + Type: dapr.StateOperationTypeUpsert, + Item: &dapr.SetStateItem{ + Key: "key1", + Value: []byte("2"), + // Override the data payload saved to the database + Metadata: map[string]string{ + "outbox.projection": "true", + "outbox.cloudevent.id": "unique-business-process-id", + "outbox.cloudevent.source": "CustomersApp", + "outbox.cloudevent.type": "CustomerCreated", + "outbox.cloudevent.subject": "123", + "outbox.cloudevent.my-custom-ce-field": "abc", + }, + }, +} +ops = append(ops, op1, op2) +meta := map[string]string{} +err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) +``` + +{{% alert title="Note" color="primary" %}} +The `outbox.cloudevent.data` metadata is reserved for Dapr's use only, and is non-customizable. + +{{% /alert %}} + ## Demo Watch [this video for an overview of the outbox pattern](https://youtu.be/rTovKpG0rhY?t=1338): From 138b777b697cd0bbb087d34e3cce52b9e281267c Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 16 Jul 2024 12:46:46 -0400 Subject: [PATCH 26/92] add http tabs and remove outbox.cloudevent.* format Signed-off-by: Hannah Hunter --- .../state-management/howto-outbox.md | 108 ++++++++++++++++-- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index 64aca9960..960827900 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -110,9 +110,15 @@ spec: ### Shape the outbox pattern message -You can override the outbox pattern message saved to the database during the transaction by setting a different message. This is done via a projected transaction payload, which is ignored in the database, but used as the outbox pattern message published to the user topic. +You can override the outbox pattern message published to the pub/sub broker by setting a different message. This is done via a projected transaction payload, which is ignored, but used as the outbox pattern message published to the user topic. -In the following Go example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. +{{< tabs "Go SDK" HTTP >}} + +{{% codetab %}} + + + +In the following Go SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. ```go ops := make([]*dapr.StateOperation, 0) @@ -140,11 +146,57 @@ meta := map[string]string{} err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) ``` -By setting the metadata item `"outbox.projection"` to `"true"`, the transaction value saved to the database is ignored, while the second value is published to the configured pub/sub topic. +By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. -### Override Dapr-generated cloudevent fields +{{% /codetab %}} -You can also override the [Dapr-generated cloudevent fields]({{< ref "pubsub-cloudevents.md#dapr-generated-cloudevents-example" >}}) on the published outbox event with custom cloudevent metadata. +{{% codetab %}} + + + +You can pass the message override using the following HTTP request: + +```bash +curl -X POST http://localhost:3500/v1.0/state/starwars/transaction \ + -H "Content-Type: application/json" \ + -d '{ + "operations": [ + { + "operation": "upsert", + "request": { + "key": "key1", + "value": "2" + } + }, + { + "operation": "upsert", + "request": { + "key": "key1" + "value: "3" + "metadata": { + "outboxProjection": "true" + } + } + } + ], + }' +``` + +By setting the metadata item `"outboxProjection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. + +{{% /codetab %}} + +{{< /tabs >}} + +### Override Dapr-generated CloudEvent fields + +You can also override the [Dapr-generated CloudEvent fields]({{< ref "pubsub-cloudevents.md#dapr-generated-cloudevents-example" >}}) on the published outbox event with custom CloudEvent metadata. + +{{< tabs "Go SDK" HTTP >}} + +{{% codetab %}} + + ```go ops := make([]*dapr.StateOperation, 0) @@ -156,12 +208,11 @@ op1 := &dapr.StateOperation{ Value: []byte("2"), // Override the data payload saved to the database Metadata: map[string]string{ - "outbox.projection": "true", - "outbox.cloudevent.id": "unique-business-process-id", - "outbox.cloudevent.source": "CustomersApp", - "outbox.cloudevent.type": "CustomerCreated", - "outbox.cloudevent.subject": "123", - "outbox.cloudevent.my-custom-ce-field": "abc", + "id": "unique-business-process-id", + "source": "CustomersApp", + "type": "CustomerCreated", + "subject": "123", + "my-custom-ce-field": "abc", }, }, } @@ -169,9 +220,42 @@ ops = append(ops, op1, op2) meta := map[string]string{} err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) ``` +{{% /codetab %}} + +{{% codetab %}} + + + +```bash +curl -X POST http://localhost:3500/v1.0/state/starwars/transaction \ + -H "Content-Type: application/json" \ + -d '{ + "operations": [ + { + "operation": "upsert", + "request": { + "key": "key1", + "value": "2" + } + }, + ], + "metadata": { + "id": "unique-business-process-id", + "source": "CustomersApp", + "type": "CustomerCreated", + "subject": "123", + "my-custom-ce-field": "abc", + } + }' +``` + +{{% /codetab %}} + +{{< /tabs >}} + {{% alert title="Note" color="primary" %}} -The `outbox.cloudevent.data` metadata is reserved for Dapr's use only, and is non-customizable. +The `data` CloudEvent field is reserved for Dapr's use only, and is non-customizable. {{% /alert %}} From 6ffd987f539eebe4c57edf00f8ba5d13715f0274 Mon Sep 17 00:00:00 2001 From: Yaron Schneider Date: Tue, 16 Jul 2024 09:58:49 -0700 Subject: [PATCH 27/92] remove redundant space --- .../building-blocks/state-management/howto-outbox.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index 960827900..a78282255 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -146,7 +146,7 @@ meta := map[string]string{} err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) ``` -By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. +By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. {{% /codetab %}} From dbaf646c57253bb73e106278dba9cce95e756a8b Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 17 Jul 2024 09:01:20 -0400 Subject: [PATCH 28/92] update image to show new link Signed-off-by: Hannah Hunter --- .../dapr-init-output.png | Bin 36770 -> 41887 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/daprdocs/static/images/install-dapr-selfhost/dapr-init-output.png b/daprdocs/static/images/install-dapr-selfhost/dapr-init-output.png index e3fde1fddc9be9139af12bf35ae1a5f2ffedbe17..61018d1502799b41fad5f658f7e5af22f72eedbf 100644 GIT binary patch literal 41887 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.e{9d%8i1<6^?MA&dakH(u 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

9_Fj$Qx8LFtmz5DF@WMM-Ur;YL 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 Date: Wed, 17 Jul 2024 20:02:56 +0100 Subject: [PATCH 29/92] Replaced wrong resiliency example definition --- .../resiliency/resiliency-state-quickstart.md | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md index 8a35d2442..674cb5dae 100644 --- a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md @@ -61,27 +61,28 @@ Run the `order-processor` service alongside a Dapr sidecar. The Dapr sidecar the metadata: name: myresiliency scopes: - - checkout - + - order-processor + spec: policies: retries: retryForever: policy: constant - maxInterval: 5s - maxRetries: -1 - + duration: 5s + maxRetries: -1 + circuitBreakers: simpleCB: maxRequests: 1 - timeout: 5s + timeout: 5s trip: consecutiveFailures >= 5 - + targets: - apps: - order-processor: - retry: retryForever - circuitBreaker: simpleCB + components: + statestore: + outbound: + retry: retryForever + circuitBreaker: simpleCB ``` From cd0a2bd7e9c0c6984aff8e8b51b6e969262649e0 Mon Sep 17 00:00:00 2001 From: Arthur Poiret Date: Thu, 18 Jul 2024 18:33:55 +0000 Subject: [PATCH 30/92] add details for single active consumer parameter Signed-off-by: Arthur Poiret --- .../supported-pubsub/setup-rabbitmq.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md index 1bb9eb75f..bf8ac3f27 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-rabbitmq.md @@ -453,7 +453,13 @@ If you set both component-level and message-level TTL, the default component-lev ## Single Active Consumer -Enable RabbitMQ [Single Active Consumer](https://www.rabbitmq.com/docs/consumers#single-active-consumer) parameter on queues. +The RabbitMQ [Single Active Consumer](https://www.rabbitmq.com/docs/consumers#single-active-consumer) setup ensures that only one consumer at a time processes messages from a queue and switches to another registered consumer if the active one is canceled or fails. This approach might be required when it is crucial for messages to be consumed in the exact order they arrive in the queue and if distributed processing with multiple instances is not supported. +When this option is enabled on a queue by Dapr, an instance of the Dapr runtime will be the single active consumer. To allow another application instance to take over in case of failure, Dapr runtime must [probe the application's health]({{< ref "app-health.md" >}}) and unsubscribe from the pub/sub component. + +{{% alert title="Note" color="primary" %}} +This pattern will prevent the application to scale as only one instance can process the load. While it might be interesting for Dapr integration with legacy or sensible applications, you should consider a design allowing distributed processing if you need scalability. +{{% /alert %}} + ```yml apiVersion: dapr.io/v2alpha1 @@ -469,10 +475,6 @@ spec: singleActiveConsumer: "true" ``` -{{% alert title="Note" color="primary" %}} -The Dapr runtime acts as the single active consumer from RabbitMQ's perspective. To allow another application instance to take over in case of failure, Dapr must [probe the application's health]({{< ref "app-health.md" >}}) and unsubscribe from pub/sub component. -{{% /alert %}} - ## Related links - [Basic schema for a Dapr component]({{< ref component-schema >}}) in the Related links section From e51f0c219a2daa24d996fb953339b2307c8bc8aa Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Thu, 18 Jul 2024 14:47:51 -0400 Subject: [PATCH 31/92] add to how-to Signed-off-by: Hannah Hunter --- .../jobs/howto-schedule-jobs.md | 94 ++++++++++++++++++- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md index 166530635..b3dda8553 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md @@ -12,18 +12,104 @@ Now that you've learned what the [jobs building block]({{< ref jobs-overview.md Include a diagram or image, if possible. --> +## Start the Scheduler service + +When you [run `dapr init` in either self-hosted mode or on Kubernetes]({{< ref install-dapr-selfhost.md >}}), the Dapr scheduler service is started. + +## Set up the Jobs API + +In your code, set up and schedule jobs within your application. + +{{< tabs ".NET" "Go" >}} + +{{% codetab %}} + + -## Set up the Scheduler service +{{% /codetab %}} -When you run `dapr init` in either self-hosted mode or on Kubernetes, the Dapr scheduler service is started. +{{% codetab %}} + + + +The Go SDK triggers the job called `daprc.Job` to schedule jobs. Job data is housed in a backup database (`"my-prod-db"`) and are called with `ScheduleJobAlpha1`. For example: + +```go +package main + +import ( + //... + + daprc "github.com/dapr/go-sdk/client" + "github.com/dapr/go-sdk/examples/dist-scheduler/api" + "github.com/dapr/go-sdk/service/common" + daprs "github.com/dapr/go-sdk/service/grpc" +) + +func main() { + + // Initialize the server + server, err := daprs.NewService(":50070") + + // ... + + ctx := context.Background() + + // Set up backup location + jobData, err := json.Marshal(&api.DBBackup{ + Task: "db-backup", + Metadata: api.Metadata{ + DBName: "my-prod-db", + BackupLocation: "/backup-dir", + }, + }, + ) + + // ... + + // Set up the job data + job := daprc.Job{ + Name: "prod-db-backup", + Schedule: "@every 1s", + Repeats: 10, + Data: &anypb.Any{ + Value: jobData, + }, + } + + // Create the client + client, err := daprc.NewClient() + //... + + // Schedule job + err = client.ScheduleJobAlpha1(ctx, &job) + + // ... + + // Get job + resp, err := client.GetJobAlpha1(ctx, "prod-db-backup") + // ... + + // Delete job + err = client.DeleteJobAlpha1(ctx, "prod-db-backup") + // .. +} + +var jobCount = 0 +``` + +{{% /codetab %}} + + +{{< /tabs >}} ## Run the Dapr sidecar -Run the Dapr sidecar alongside your application. +Once you've set up the Jobs API in your application, run the Dapr sidecar. ```bash -dapr run --app-id=jobs --app-port 50070 --app-protocol grpc --log-level debug -- go run main.go +dapr run --app-id=distributed-scheduler --metrics-port=9091 --scheduler-host-address=localhost:50006 --dapr-grpc-port 50001 --app-port 50070 --app-protocol grpc --log-level debug go run ./main.go ``` ## Next steps From 32fce1da8ff8ed03fae3bdfad73e4092f3c5042a Mon Sep 17 00:00:00 2001 From: Bilgin Ibryam Date: Fri, 19 Jul 2024 22:42:27 +0100 Subject: [PATCH 32/92] Fixed typo in links (#4267) --- .../building-blocks/secrets/secrets-scopes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md b/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md index 167d85f98..afd417515 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md +++ b/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md @@ -69,7 +69,7 @@ spec: allowedSecrets: ["secret1", "secret2"] ``` -The default access to the `vault` secret store is `deny`, while some secrets are accessible by the application, based on the `allowedSecrets` list. [Learn how to apply configuration to the sidecar]]({{< ref configuration-concept.md >}}). +The default access to the `vault` secret store is `deny`, while some secrets are accessible by the application, based on the `allowedSecrets` list. [Learn how to apply configuration to the sidecar]({{< ref configuration-concept.md >}}). ## Scenario 3: Deny access to certain sensitive secrets in a secret store @@ -88,7 +88,7 @@ spec: deniedSecrets: ["secret1", "secret2"] ``` -This example configuration explicitly denies access to `secret1` and `secret2` from the secret store named `vault` while allowing access to all other secrets. [Learn how to apply configuration to the sidecar]]({{< ref configuration-concept.md >}}). +This example configuration explicitly denies access to `secret1` and `secret2` from the secret store named `vault` while allowing access to all other secrets. [Learn how to apply configuration to the sidecar]({{< ref configuration-concept.md >}}). ## Permission priority From 1fbc73e3115132e5b43455a6a928d219c5d9e7f9 Mon Sep 17 00:00:00 2001 From: Bilgin Ibryam Date: Fri, 19 Jul 2024 22:42:27 +0100 Subject: [PATCH 33/92] Fixed typo in links (#4267) Signed-off-by: Erwin Kramer --- .../building-blocks/secrets/secrets-scopes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md b/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md index 167d85f98..afd417515 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md +++ b/daprdocs/content/en/developing-applications/building-blocks/secrets/secrets-scopes.md @@ -69,7 +69,7 @@ spec: allowedSecrets: ["secret1", "secret2"] ``` -The default access to the `vault` secret store is `deny`, while some secrets are accessible by the application, based on the `allowedSecrets` list. [Learn how to apply configuration to the sidecar]]({{< ref configuration-concept.md >}}). +The default access to the `vault` secret store is `deny`, while some secrets are accessible by the application, based on the `allowedSecrets` list. [Learn how to apply configuration to the sidecar]({{< ref configuration-concept.md >}}). ## Scenario 3: Deny access to certain sensitive secrets in a secret store @@ -88,7 +88,7 @@ spec: deniedSecrets: ["secret1", "secret2"] ``` -This example configuration explicitly denies access to `secret1` and `secret2` from the secret store named `vault` while allowing access to all other secrets. [Learn how to apply configuration to the sidecar]]({{< ref configuration-concept.md >}}). +This example configuration explicitly denies access to `secret1` and `secret2` from the secret store named `vault` while allowing access to all other secrets. [Learn how to apply configuration to the sidecar]({{< ref configuration-concept.md >}}). ## Permission priority From bc8303e82ab4310f7863328939f7a79c8e5175fb Mon Sep 17 00:00:00 2001 From: Erwin Kramer Date: Sat, 20 Jul 2024 15:40:16 +0200 Subject: [PATCH 34/92] markdown linting and navigation issues Signed-off-by: Erwin Kramer --- .../quickstarts/workflow-quickstart.md | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md index 8e1adb0c5..f99d0ebd1 100644 --- a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md @@ -7,10 +7,10 @@ description: Get started with the Dapr Workflow building block --- {{% alert title="Note" color="primary" %}} -Dapr Workflow is currently in beta. [See known limitations for {{% dapr-latest-version cli="true" %}}]({{< ref "workflow-overview.md#limitations" >}}). +Dapr Workflow is currently in beta. [See known limitations for {{% dapr-latest-version cli="true" %}}]({{< ref "workflow-overview.md#limitations" >}}). {{% /alert %}} -Let's take a look at the Dapr [Workflow building block]({{< ref workflow-overview.md >}}). In this Quickstart, you'll create a simple console application to demonstrate Dapr's workflow programming model and the workflow management APIs. +Let's take a look at the Dapr [Workflow building block]({{< ref workflow-overview.md >}}). In this Quickstart, you'll create a simple console application to demonstrate Dapr's workflow programming model and the workflow management APIs. In this guide, you'll: @@ -29,8 +29,8 @@ Select your preferred language-specific Dapr SDK before proceeding with the Quic The `order-processor` console app starts and manages the `order_processing_workflow`, which simulates purchasing items from a store. The workflow consists of five unique workflow activities, or tasks: - `notify_activity`: Utilizes a logger to print out messages throughout the workflow. These messages notify you when: - - You have insufficient inventory - - Your payment couldn't be processed, etc. + - You have insufficient inventory + - Your payment couldn't be processed, etc. - `process_payment_activity`: Processes and authorizes the payment. - `verify_inventory_activity`: Checks the state store to ensure there is enough inventory present for purchase. - `update_inventory_activity`: Removes the requested items from the state store and updates the store with the new remaining inventory value. @@ -71,10 +71,11 @@ pip3 install -r requirements.txt In the terminal, start the order processor app alongside a Dapr sidecar using [Multi-App Run]({{< ref multi-app-dapr-run >}}): ```bash +cd workflows/python/sdk dapr run -f . ``` -This starts the `order-processor` app with unique workflow ID and runs the workflow activities. +This starts the `order-processor` app with unique workflow ID and runs the workflow activities. Expected output: @@ -105,7 +106,7 @@ Running `dapr init` launches the [openzipkin/zipkin](https://hub.docker.com/r/op docker run -d -p 9411:9411 openzipkin/zipkin ``` -View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). +View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). @@ -122,9 +123,10 @@ When you ran `dapr run -f .`: 1. The `NotifyActivity` workflow activity sends a notification saying that order `f4e1926e-3721-478d-be8a-f5bebd1995da` has completed. 1. The workflow terminates as completed. -#### `order-processor/app.py` +#### `order-processor/app.py` In the application's program file: + - The unique workflow order ID is generated - The workflow is scheduled - The workflow status is retrieved @@ -276,7 +278,6 @@ The `order-processor` console app starts and manages the lifecycle of an order p - `processPaymentActivity`: Processes and authorizes the payment. - `updateInventoryActivity`: Updates the state store with the new remaining inventory value. - ### Step 1: Pre-requisites For this example, you will need: @@ -318,11 +319,11 @@ In the terminal, start the order processor app alongside a Dapr sidecar using [M dapr run -f . ``` -This starts the `order-processor` app with unique workflow ID and runs the workflow activities. +This starts the `order-processor` app with unique workflow ID and runs the workflow activities. Expected output: -``` +```log == APP - workflowApp == == APP == Orchestration scheduled with ID: 0c332155-1e02-453a-a333-28cfc7777642 == APP - workflowApp == == APP == Waiting 30 seconds for instance 0c332155-1e02-453a-a333-28cfc7777642 to complete... == APP - workflowApp == == APP == Received "Orchestrator Request" work item with instance id '0c332155-1e02-453a-a333-28cfc7777642' @@ -393,7 +394,7 @@ Running `dapr init` launches the [openzipkin/zipkin](https://hub.docker.com/r/op docker run -d -p 9411:9411 openzipkin/zipkin ``` -View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). +View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). @@ -410,9 +411,10 @@ When you ran `dapr run -f .`: 1. The `notifyActivity` workflow activity sends a notification saying that order `0c332155-1e02-453a-a333-28cfc7777642` has completed. 1. The workflow terminates as completed. -#### `order-processor/workflowApp.ts` +#### `order-processor/workflowApp.ts` In the application file: + - The unique workflow order ID is generated - The workflow is scheduled - The workflow status is retrieved @@ -489,12 +491,12 @@ start().catch((e) => { {{% codetab %}} The `order-processor` console app starts and manages the lifecycle of an order processing workflow that stores and retrieves data in a state store. The workflow consists of four workflow activities, or tasks: + - `NotifyActivity`: Utilizes a logger to print out messages throughout the workflow - `ReserveInventoryActivity`: Checks the state store to ensure that there is enough inventory for the purchase - `ProcessPaymentActivity`: Processes and authorizes the payment - `UpdateInventoryActivity`: Removes the requested items from the state store and updates the store with the new remaining inventory value - ### Step 1: Pre-requisites For this example, you will need: @@ -513,10 +515,10 @@ Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quic git clone https://github.com/dapr/quickstarts.git ``` -In a new terminal window, navigate to the `order-processor` directory: +In a new terminal window, navigate to the `sdk` directory: ```bash -cd workflows/csharp/sdk/order-processor +cd workflows/csharp/sdk ``` ### Step 3: Run the order processor app @@ -527,7 +529,7 @@ In the terminal, start the order processor app alongside a Dapr sidecar using [M dapr run -f . ``` -This starts the `order-processor` app with unique workflow ID and runs the workflow activities. +This starts the `order-processor` app with unique workflow ID and runs the workflow activities. Expected output: @@ -567,7 +569,7 @@ Running `dapr init` launches the [openzipkin/zipkin](https://hub.docker.com/r/op docker run -d -p 9411:9411 openzipkin/zipkin ``` -View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). +View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). @@ -584,9 +586,10 @@ When you ran `dapr run -f .`: 1. The `NotifyActivity` workflow activity sends a notification saying that order `6d2abcc9` has completed. 1. The workflow terminates as completed. -#### `order-processor/Program.cs` +#### `order-processor/Program.cs` In the application's program file: + - The unique workflow order ID is generated - The workflow is scheduled - The workflow status is retrieved @@ -717,6 +720,7 @@ class OrderProcessingWorkflow : Workflow #### `order-processor/Activities` directory The `Activities` directory holds the four workflow activities used by the workflow, defined in the following files: + - `NotifyActivity.cs` - `ReserveInventoryActivity.cs` - `ProcessPaymentActivity.cs` @@ -734,22 +738,22 @@ Watch [this video to walk through the Dapr Workflow .NET demo](https://youtu.be/ {{% codetab %}} The `order-processor` console app starts and manages the lifecycle of an order processing workflow that stores and retrieves data in a state store. The workflow consists of four workflow activities, or tasks: + - `NotifyActivity`: Utilizes a logger to print out messages throughout the workflow - `RequestApprovalActivity`: Requests approval for processing payment - `ReserveInventoryActivity`: Checks the state store to ensure that there is enough inventory for the purchase - `ProcessPaymentActivity`: Processes and authorizes the payment - `UpdateInventoryActivity`: Removes the requested items from the state store and updates the store with the new remaining inventory value - ### Step 1: Pre-requisites For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). - Java JDK 11 (or greater): - - [Microsoft JDK 11](https://docs.microsoft.com/java/openjdk/download#openjdk-11) - - [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) - - [OpenJDK 11](https://jdk.java.net/11/) + - [Microsoft JDK 11](https://docs.microsoft.com/java/openjdk/download#openjdk-11) + - [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) + - [OpenJDK 11](https://jdk.java.net/11/) - [Apache Maven](https://maven.apache.org/install.html) version 3.x. - [Docker Desktop](https://www.docker.com/products/docker-desktop) @@ -780,10 +784,11 @@ mvn clean install In the terminal, start the order processor app alongside a Dapr sidecar using [Multi-App Run]({{< ref multi-app-dapr-run >}}): ```bash +cd workflows/java/sdk dapr run -f . ``` -This starts the `order-processor` app with unique workflow ID and runs the workflow activities. +This starts the `order-processor` app with unique workflow ID and runs the workflow activities. Expected output: @@ -826,7 +831,7 @@ Running `dapr init` launches the [openzipkin/zipkin](https://hub.docker.com/r/op docker run -d -p 9411:9411 openzipkin/zipkin ``` -View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). +View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). @@ -1073,7 +1078,6 @@ The `Activities` directory holds the four workflow activities used by the workfl {{% codetab %}} - The `order-processor` console app starts and manages the `OrderProcessingWorkflow` workflow, which simulates purchasing items from a store. The workflow consists of five unique workflow activities, or tasks: - `NotifyActivity`: Utilizes a logger to print out messages throughout the workflow. These messages notify you when: @@ -1102,10 +1106,10 @@ Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quic git clone https://github.com/dapr/quickstarts.git ``` -In a new terminal window, navigate to the `order-processor` directory: +In a new terminal window, navigate to the `sdk` directory: ```bash -cd workflows/go/sdk/order-processor +cd workflows/go/sdk ``` ### Step 3: Run the order processor app @@ -1116,7 +1120,7 @@ In the terminal, start the order processor app alongside a Dapr sidecar using [M dapr run -f . ``` -This starts the `order-processor` app with unique workflow ID and runs the workflow activities. +This starts the `order-processor` app with unique workflow ID and runs the workflow activities. Expected output: @@ -1157,7 +1161,7 @@ Running `dapr init` launches the [openzipkin/zipkin](https://hub.docker.com/r/op docker run -d -p 9411:9411 openzipkin/zipkin ``` -View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). +View the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`). @@ -1174,9 +1178,10 @@ When you ran `dapr run`: 1. The `NotifyActivity` workflow activity sends a notification saying that order `48ee83b7-5d80-48d5-97f9-6b372f5480a5` has completed. 1. The workflow terminates as completed. -#### `order-processor/main.go` +#### `order-processor/main.go` In the application's program file: + - The unique workflow order ID is generated - The workflow is scheduled - The workflow status is retrieved @@ -1317,6 +1322,7 @@ Meanwhile, the `OrderProcessingWorkflow` and its activities are defined as metho {{< /tabs >}} ## Tell us what you think! + We're continuously working to improve our Quickstart examples and value your feedback. Did you find this Quickstart helpful? Do you have suggestions for improvement? Join the discussion in our [discord channel](https://discord.com/channels/778680217417809931/953427615916638238). From d48caef391fccc7b5cb7bd517fdb489f14489902 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 22 Jul 2024 15:16:39 -0400 Subject: [PATCH 35/92] update with other code examples and add additional instructions Signed-off-by: Hannah Hunter --- .../state-management/howto-outbox.md | 278 +++++++++++++++++- 1 file changed, 276 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index a78282255..8f91acf64 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -112,7 +112,160 @@ spec: You can override the outbox pattern message published to the pub/sub broker by setting a different message. This is done via a projected transaction payload, which is ignored, but used as the outbox pattern message published to the user topic. -{{< tabs "Go SDK" HTTP >}} +In order to override, the `key` values must match between the operation on the state store and the message projection. If the keys do not match, the whole transaction fails. + +If you have two or more `outboxProjections` for the same key, the first one defined is used and the others are ignored. + +{{< tabs JavaScript ".NET" Java Go HTTP >}} + +{{% codetab %}} + + + +In the following .NET SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. + +```javascript +const { DaprClient, StateOperationType } = require('@dapr/dapr'); + +const DAPR_STORE_NAME = "statestore"; + +async function main() { + const client = new DaprClient(); + + // Define the first state operation to save the value "2" + const op1 = { + operation: StateOperationType.UPSERT, + request: { + key: "key1", + value: "2" + } + }; + + // Define the second state operation to publish the value "3" with metadata + const op2 = { + operation: StateOperationType.UPSERT, + request: { + key: "key1", + value: "3", + metadata: { + "outbox.projection": "true" + } + } + }; + + // Create the list of state operations + const ops = [op1, op2]; + + // Execute the state transaction + await client.state.transaction(DAPR_STORE_NAME, ops); + console.log("State transaction executed."); +} + +main().catch(err => { + console.error(err); +}); +``` + +By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. + +{{% /codetab %}} + +{{% codetab %}} + + + +In the following .NET SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. + +```csharp +public class Program +{ + private const string DAPR_STORE_NAME = "statestore"; + + public static async Task Main(string[] args) + { + var client = new DaprClientBuilder().Build(); + + // Define the first state operation to save the value "2" + var op1 = new StateTransactionRequest( + key: "key1", + value: Encoding.UTF8.GetBytes("2"), + operationType: StateOperationType.Upsert + ); + + // Define the second state operation to publish the value "3" with metadata + var metadata = new Dictionary + { + { "outbox.projection", "true" } + }; + var op2 = new StateTransactionRequest( + key: "key1", + value: Encoding.UTF8.GetBytes("3"), + operationType: StateOperationType.Upsert, + metadata: metadata + ); + + // Create the list of state operations + var ops = new List { op1, op2 }; + + // Execute the state transaction + await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, ops); + Console.WriteLine("State transaction executed."); + } +} +``` + +By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. + +{{% /codetab %}} + +{{% codetab %}} + + + +In the following Go SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. + +```java +public class Main { + private static final String DAPR_STORE_NAME = "statestore"; + + public static void main(String[] args) { + try (DaprClient client = new DaprClientBuilder().build()) { + // Define the first state operation to save the value "2" + StateOperation op1 = new StateOperation<>( + StateOperationType.UPSERT, + "key1", + "2" + ); + + // Define the second state operation to publish the value "3" with metadata + Map metadata = new HashMap<>(); + metadata.put("outbox.projection", "true"); + + StateOperation op2 = new StateOperation<>( + StateOperationType.UPSERT, + "key1", + "3", + metadata + ); + + // Create the list of state operations + List> ops = new ArrayList<>(); + ops.add(op1); + ops.add(op2); + + // Execute the state transaction + client.executeStateTransaction(DAPR_STORE_NAME, ops).block(); + System.out.println("State transaction executed."); + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. + +{{% /codetab %}} {{% codetab %}} @@ -192,7 +345,128 @@ By setting the metadata item `"outboxProjection"` to `"true"`, the first transa You can also override the [Dapr-generated CloudEvent fields]({{< ref "pubsub-cloudevents.md#dapr-generated-cloudevents-example" >}}) on the published outbox event with custom CloudEvent metadata. -{{< tabs "Go SDK" HTTP >}} +{{< tabs JavaScript ".NET" Java Go HTTP >}} + +{{% codetab %}} + + + +```javascript +const { DaprClient } = require('dapr-client'); + +async function executeStateTransaction() { + // Initialize Dapr client + const daprClient = new DaprClient(); + + // Define state operations + const ops = []; + + const op1 = { + operationType: 'upsert', + request: { + key: 'key1', + value: Buffer.from('2'), + metadata: { + 'id': 'unique-business-process-id', + 'source': 'CustomersApp', + 'type': 'CustomerCreated', + 'subject': '123', + 'my-custom-ce-field': 'abc' + } + } + }; + + ops.push(op1); + + // Execute state transaction + const storeName = 'your-state-store-name'; + const metadata = {}; +} + +executeStateTransaction(); +``` +{{% /codetab %}} + +{{% codetab %}} + + + +```csharp +public class StateOperationExample +{ + public async Task ExecuteStateTransactionAsync() + { + var daprClient = new DaprClientBuilder().Build(); + + // Define state operations + var ops = new List(); + + var op1 = new StateOperation + { + OperationType = StateOperationType.Upsert, + Request = new SetStateRequest + { + Key = "key1", + Value = new byte[] { 50 }, // []byte("2") in Go is equivalent to new byte[] { 50 } in C# + Metadata = new Dictionary + { + { "id", "unique-business-process-id" }, + { "source", "CustomersApp" }, + { "type", "CustomerCreated" }, + { "subject", "123" }, + { "my-custom-ce-field", "abc" } + } + } + }; + + ops.Add(op1); + + // Execute state transaction + var storeName = "your-state-store-name"; + var metadata = new Dictionary(); + } +} +``` +{{% /codetab %}} + +{{% codetab %}} + + + +```java +public class StateOperationExample { + + public static void main(String[] args) { + executeStateTransaction(); + } + + public static void executeStateTransaction() { + // Build Dapr client + DaprClient daprClient = new DaprClientBuilder().build(); + + // Define state operations + List> ops = new ArrayList<>(); + + State op1 = new State<>( + "key1", + "2", + new StateOptions<>(StateOperation.Type.UPSERT, new HashMap() {{ + put("id", "unique-business-process-id"); + put("source", "CustomersApp"); + put("type", "CustomerCreated"); + put("subject", "123"); + put("my-custom-ce-field", "abc"); + }}) + ); + ops.add(op1); + + // Execute state transaction + String storeName = "your-state-store-name"; + Map metadata = new HashMap<>(); + } +} +``` +{{% /codetab %}} {{% codetab %}} From 6ba580a0d3068d15b5fed9dbd9e2dcb5a8d14bdd Mon Sep 17 00:00:00 2001 From: Jakub Bartkowiak Date: Tue, 23 Jul 2024 10:54:09 +0200 Subject: [PATCH 36/92] Update install-dapr-cli.md Add a note that Docker Desktop's advanced options section is not visible when WSL integration is enabled. Signed-off-by: Jakub Bartkowiak --- daprdocs/content/en/getting-started/install-dapr-cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/getting-started/install-dapr-cli.md b/daprdocs/content/en/getting-started/install-dapr-cli.md index 21f179689..7ab5149ce 100644 --- a/daprdocs/content/en/getting-started/install-dapr-cli.md +++ b/daprdocs/content/en/getting-started/install-dapr-cli.md @@ -16,7 +16,7 @@ You'll use the Dapr CLI as the main tool for various Dapr-related tasks. You can The Dapr CLI works with both [self-hosted]({{< ref self-hosted >}}) and [Kubernetes]({{< ref Kubernetes >}}) environments. {{% alert title="Before you begin" color="primary" %}} -In Docker Desktop's advanced options, verify you've allowed the default Docker socket to be used. +In Docker Desktop's advanced options, verify you've allowed the default Docker socket to be used. This option is not available if your are using WSL integration on Windows. {{% /alert %}} From 04d786afbab2b1bc4ea4f407ad7cecf9117e24bf Mon Sep 17 00:00:00 2001 From: Jakub Bartkowiak Date: Tue, 23 Jul 2024 15:12:15 +0200 Subject: [PATCH 37/92] Update daprdocs/content/en/getting-started/install-dapr-cli.md Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Jakub Bartkowiak --- daprdocs/content/en/getting-started/install-dapr-cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/getting-started/install-dapr-cli.md b/daprdocs/content/en/getting-started/install-dapr-cli.md index 7ab5149ce..da8dee54e 100644 --- a/daprdocs/content/en/getting-started/install-dapr-cli.md +++ b/daprdocs/content/en/getting-started/install-dapr-cli.md @@ -16,7 +16,7 @@ You'll use the Dapr CLI as the main tool for various Dapr-related tasks. You can The Dapr CLI works with both [self-hosted]({{< ref self-hosted >}}) and [Kubernetes]({{< ref Kubernetes >}}) environments. {{% alert title="Before you begin" color="primary" %}} -In Docker Desktop's advanced options, verify you've allowed the default Docker socket to be used. This option is not available if your are using WSL integration on Windows. +In Docker Desktop's advanced options, verify you've allowed the default Docker socket to be used. This option is not available if you are using WSL integration on Windows. {{% /alert %}} From 52289a9499d74b49966d355e6c1870748e189637 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 09:20:48 -0400 Subject: [PATCH 38/92] updates per mark review Signed-off-by: Hannah Hunter --- .../state-management/howto-outbox.md | 273 +++++++++++++----- 1 file changed, 205 insertions(+), 68 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index 8f91acf64..a2facf91b 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -110,19 +110,64 @@ spec: ### Shape the outbox pattern message -You can override the outbox pattern message published to the pub/sub broker by setting a different message. This is done via a projected transaction payload, which is ignored, but used as the outbox pattern message published to the user topic. +You can override the outbox pattern message published to the pub/sub broker by setting a different message. This is done via a projected transaction payload, called `outboxProjections`, which is ignored when the state is written and is used as the outbox pattern message published to the user topic. -In order to override, the `key` values must match between the operation on the state store and the message projection. If the keys do not match, the whole transaction fails. +To set the `outboxProjections` to `true`, the `key` values must match between the operation on the state store and the message projection. If the keys do not match, the whole transaction fails. If you have two or more `outboxProjections` for the same key, the first one defined is used and the others are ignored. -{{< tabs JavaScript ".NET" Java Go HTTP >}} +[Learn more about default and custom CloudEvent messages.]({{< ref pubsub-cloudevents.md >}}) + +{{< tabs Python JavaScript ".NET" Java Go HTTP >}} + +{{% codetab %}} + + + +In the following Python SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. + +```python +DAPR_STORE_NAME = "statestore" + +async def main(): + client = DaprClient() + + # Define the first state operation to save the value "2" + op1 = StateItem( + key="key1", + value=b"2" + ) + + # Define the second state operation to publish the value "3" with metadata + op2 = StateItem( + key="key1", + value=b"3", + options=StateOptions( + metadata={ + "outbox.projection": "true" + } + ) + ) + + # Create the list of state operations + ops = [op1, op2] + + # Execute the state transaction + await client.state.transaction(DAPR_STORE_NAME, operations=ops) + print("State transaction executed.") +``` + +By setting the metadata item `"outbox.projection"` to `"true"` and making sure the `key` values match (`key1`): +- The first operation is written to the state store and no message is written to the message broker. +- The second operation value is published to the configured pub/sub topic. + +{{% /codetab %}} {{% codetab %}} -In the following .NET SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. +In the following JavaScript SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. ```javascript const { DaprClient, StateOperationType } = require('@dapr/dapr'); @@ -166,7 +211,10 @@ main().catch(err => { }); ``` -By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. +By setting the metadata item `"outbox.projection"` to `"true"` and making sure the `key` values match (`key1`): +- The first operation is written to the state store and no message is written to the message broker. +- The second operation value is published to the configured pub/sub topic. + {{% /codetab %}} @@ -214,7 +262,9 @@ public class Program } ``` -By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. +By setting the metadata item `"outbox.projection"` to `"true"` and making sure the `key` values match (`key1`): +- The first operation is written to the state store and no message is written to the message broker. +- The second operation value is published to the configured pub/sub topic. {{% /codetab %}} @@ -222,7 +272,7 @@ By setting the metadata item `"outbox.projection"` to `"true"`, the first transa -In the following Go SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. +In the following Java SDK example of a state transaction, the value of `"2"` is saved to the database, but the value of `"3"` is published to the end-user topic. ```java public class Main { @@ -263,7 +313,10 @@ public class Main { } ``` -By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. +By setting the metadata item `"outbox.projection"` to `"true"` and making sure the `key` values match (`key1`): +- The first operation is written to the state store and no message is written to the message broker. +- The second operation value is published to the configured pub/sub topic. + {{% /codetab %}} @@ -299,7 +352,9 @@ meta := map[string]string{} err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) ``` -By setting the metadata item `"outbox.projection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. +By setting the metadata item `"outbox.projection"` to `"true"` and making sure the `key` values match (`key1`): +- The first operation is written to the state store and no message is written to the message broker. +- The second operation value is published to the configured pub/sub topic. {{% /codetab %}} @@ -335,7 +390,9 @@ curl -X POST http://localhost:3500/v1.0/state/starwars/transaction \ }' ``` -By setting the metadata item `"outboxProjection"` to `"true"`, the first transaction value published to the broker is ignored, while the second value is published to the configured pub/sub topic. +By setting the metadata item `"outbox.projection"` to `"true"` and making sure the `key` values match (`key1`): +- The first operation is written to the state store and no message is written to the message broker. +- The second operation value is published to the configured pub/sub topic. {{% /codetab %}} @@ -343,9 +400,50 @@ By setting the metadata item `"outboxProjection"` to `"true"`, the first transa ### Override Dapr-generated CloudEvent fields -You can also override the [Dapr-generated CloudEvent fields]({{< ref "pubsub-cloudevents.md#dapr-generated-cloudevents-example" >}}) on the published outbox event with custom CloudEvent metadata. +You can override the [Dapr-generated CloudEvent fields]({{< ref "pubsub-cloudevents.md#dapr-generated-cloudevents-example" >}}) on the published outbox event with custom CloudEvent metadata. -{{< tabs JavaScript ".NET" Java Go HTTP >}} +{{< tabs Python JavaScript ".NET" Java Go HTTP >}} + +{{% codetab %}} + + + +```python +async def execute_state_transaction(): + async with DaprClient() as client: + # Define state operations + ops = [] + + op1 = { + 'operation': 'upsert', + 'request': { + 'key': 'key1', + 'value': b'2', # Convert string to byte array + 'metadata': { + 'cloudevent.id': 'unique-business-process-id', + 'cloudevent.source': 'CustomersApp', + 'cloudevent.type': 'CustomerCreated', + 'cloudevent.subject': '123', + 'my-custom-ce-field': 'abc' + } + } + } + + ops.append(op1) + + # Execute state transaction + store_name = 'your-state-store-name' + try: + await client.execute_state_transaction(store_name, ops) + print('State transaction executed.') + except Exception as e: + print('Error executing state transaction:', e) + +# Run the async function +if __name__ == "__main__": + asyncio.run(execute_state_transaction()) +``` +{{% /codetab %}} {{% codetab %}} @@ -398,32 +496,41 @@ public class StateOperationExample { var daprClient = new DaprClientBuilder().Build(); - // Define state operations - var ops = new List(); + // Define the value "2" as a string and serialize it to a byte array + var value = "2"; + var valueBytes = JsonSerializer.SerializeToUtf8Bytes(value); - var op1 = new StateOperation + // Define the first state operation to save the value "2" with metadata + // Override Cloudevent metadata + var metadata = new Dictionary { - OperationType = StateOperationType.Upsert, - Request = new SetStateRequest - { - Key = "key1", - Value = new byte[] { 50 }, // []byte("2") in Go is equivalent to new byte[] { 50 } in C# - Metadata = new Dictionary - { - { "id", "unique-business-process-id" }, - { "source", "CustomersApp" }, - { "type", "CustomerCreated" }, - { "subject", "123" }, - { "my-custom-ce-field", "abc" } - } - } + { "cloudevent.id", "unique-business-process-id" }, + { "cloudevent.source", "CustomersApp" }, + { "cloudevent.type", "CustomerCreated" }, + { "cloudevent.subject", "123" }, + { "my-custom-ce-field", "abc" } }; - ops.Add(op1); + var op1 = new StateTransactionRequest( + key: "key1", + value: valueBytes, + operationType: StateOperationType.Upsert, + metadata: metadata + ); - // Execute state transaction + // Create the list of state operations + var ops = new List { op1 }; + + // Execute the state transaction var storeName = "your-state-store-name"; - var metadata = new Dictionary(); + await daprClient.ExecuteStateTransactionAsync(storeName, ops); + Console.WriteLine("State transaction executed."); + } + + public static async Task Main(string[] args) + { + var example = new StateOperationExample(); + await example.ExecuteStateTransactionAsync(); } } ``` @@ -442,27 +549,36 @@ public class StateOperationExample { public static void executeStateTransaction() { // Build Dapr client - DaprClient daprClient = new DaprClientBuilder().build(); + try (DaprClient daprClient = new DaprClientBuilder().build()) { - // Define state operations - List> ops = new ArrayList<>(); + // Define the value "2" + String value = "2"; - State op1 = new State<>( - "key1", - "2", - new StateOptions<>(StateOperation.Type.UPSERT, new HashMap() {{ - put("id", "unique-business-process-id"); - put("source", "CustomersApp"); - put("type", "CustomerCreated"); - put("subject", "123"); - put("my-custom-ce-field", "abc"); - }}) - ); - ops.add(op1); + // Override CloudEvent metadata + Map metadata = new HashMap<>(); + metadata.put("cloudevent.id", "unique-business-process-id"); + metadata.put("cloudevent.source", "CustomersApp"); + metadata.put("cloudevent.type", "CustomerCreated"); + metadata.put("cloudevent.subject", "123"); + metadata.put("my-custom-ce-field", "abc"); - // Execute state transaction - String storeName = "your-state-store-name"; - Map metadata = new HashMap<>(); + // Define state operations + List> ops = new ArrayList<>(); + StateOperation op1 = new StateOperation<>( + StateOperationType.UPSERT, + "key1", + value, + metadata + ); + ops.add(op1); + + // Execute state transaction + String storeName = "your-state-store-name"; + daprClient.executeStateTransaction(storeName, ops).block(); + System.out.println("State transaction executed."); + } catch (Exception e) { + e.printStackTrace(); + } } } ``` @@ -473,26 +589,47 @@ public class StateOperationExample { ```go -ops := make([]*dapr.StateOperation, 0) +func main() { + // Create a Dapr client + client, err := dapr.NewClient() + if err != nil { + log.Fatalf("failed to create Dapr client: %v", err) + } + defer client.Close() -op1 := &dapr.StateOperation{ - Type: dapr.StateOperationTypeUpsert, - Item: &dapr.SetStateItem{ - Key: "key1", - Value: []byte("2"), - // Override the data payload saved to the database - Metadata: map[string]string{ - "id": "unique-business-process-id", - "source": "CustomersApp", - "type": "CustomerCreated", - "subject": "123", - "my-custom-ce-field": "abc", - }, - }, + ctx := context.Background() + store := "your-state-store-name" + + // Define state operations + ops := make([]*dapr.StateOperation, 0) + op1 := &dapr.StateOperation{ + Type: dapr.StateOperationTypeUpsert, + Item: &dapr.SetStateItem{ + Key: "key1", + Value: []byte("2"), + // Override Cloudevent metadata + Metadata: map[string]string{ + "cloudevent.id": "unique-business-process-id", + "cloudevent.source": "CustomersApp", + "cloudevent.type": "CustomerCreated", + "cloudevent.subject": "123", + "my-custom-ce-field": "abc", + }, + }, + } + ops = append(ops, op1) + + // Metadata for the transaction (if any) + meta := map[string]string{} + + // Execute state transaction + err = client.ExecuteStateTransaction(ctx, store, meta, ops) + if err != nil { + log.Fatalf("failed to execute state transaction: %v", err) + } + + log.Println("State transaction executed.") } -ops = append(ops, op1, op2) -meta := map[string]string{} -err := testClient.ExecuteStateTransaction(ctx, store, meta, ops) ``` {{% /codetab %}} From f6b9e5f3b66b60f23b1b03436ff7f0f64eea9b61 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 09:46:00 -0400 Subject: [PATCH 39/92] update dotnet and python for now Signed-off-by: Hannah Hunter --- .../content/en/operations/support/support-release-policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index 5cc75ce95..1ecc51f0c 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,7 +45,7 @@ The table below shows the versions of Dapr releases that have been tested togeth | Release date | Runtime | CLI | SDKs | Dashboard | Status | Release notes | |--------------------|:--------:|:--------|---------|---------|---------|------------| -| July 24th 2024 | 1.14.0
| 1.14.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | +| July 24th 2024 | 1.14.0
| 1.14.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | | May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | | May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | | April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | From b953f5ec48b213e616b85163b32c77961481396b Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 10:15:28 -0400 Subject: [PATCH 40/92] fix docs Signed-off-by: Hannah Hunter --- .../configuration/configuration-overview.md | 11 +- .../observability/metrics/metrics-overview.md | 130 +++++++++++++++++- 2 files changed, 136 insertions(+), 5 deletions(-) diff --git a/daprdocs/content/en/operations/configuration/configuration-overview.md b/daprdocs/content/en/operations/configuration/configuration-overview.md index ccc126b42..0cba2414c 100644 --- a/daprdocs/content/en/operations/configuration/configuration-overview.md +++ b/daprdocs/content/en/operations/configuration/configuration-overview.md @@ -111,6 +111,15 @@ metrics: latencyDistributionBuckets: [] http: increasedCardinality: true + pathMatching: + - /items + - /orders/{orderID} + - /orders/{orderID}/items/{itemID} + - /payments/{paymentID} + - /payments/{paymentID}/status + - /payments/{paymentID}/refund + - /payments/{paymentID}/details + excludeVerbs: false ``` In the examples above this path filter `/orders/{orderID}/items/{itemID}` would return a single metric count matching all the orderIDs and all the itemIDs rather than multiple metrics for each itemID. For more information see [HTTP metrics path matching]({{< ref "metrics-overview.md#http-metrics-path-matching" >}}) @@ -126,7 +135,7 @@ The following table lists the properties for metrics: | `http.pathMatching` | array | Array of paths for path matching, allowing users to define matching paths to manage cardinality. | | `http.excludeVerbs` | boolean | When set to true (default is false), the Dapr HTTP server ignores each request HTTP verb when building the method metric label. | -To mitigate high memory usage and egress costs associated with [high cardinality metrics]({{< ref "metrics-overview.md#high-cardinality-metrics" >}}) with the HTTP server, you should set the `metrics.http.increasedCardinality` property to `false`. +To further help managing cardinality, path matching allows specified paths matched according to defined patterns, reducing the number of unique metrics paths and thus controlling metric cardinality. This feature is particularly useful for applications with dynamic URLs, ensuring that metrics remain meaningful and manageable without excessive memory consumption. Using rules, you can set regular expressions for every metric exposed by the Dapr sidecar. For example: diff --git a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md index e99f8eb47..cc7e606d3 100644 --- a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md +++ b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md @@ -70,11 +70,133 @@ spec: enabled: false ``` -## High cardinality metrics +## Optimizing HTTP metrics reporting with path matching -When invoking Dapr using HTTP, the legacy behavior (and current default as of Dapr 1.13) is to create a separate "bucket" for each requested method. When working with RESTful APIs, this can cause very high cardinality, with potential negative impact on memory usage and CPU. +When invoking Dapr using HTTP, metrics are created for each requested method by default. This can result in a high number of metrics, known as high cardinality, which can impact memory usage and CPU. -Dapr 1.13 introduces a new option for the Dapr Configuration resource `spec.metrics.http.increasedCardinality`: when set to `false`, it reports metrics for the HTTP server for each "abstract" method (for example, requesting from a state store) instead of creating a "bucket" for each concrete request path. +Path matching allows you to manage and control the cardinality of HTTP metrics in Dapr. This is an aggregation of metrics, so rather than having a metric for each event, you can reduce the number of metrics events and report an overall number. For details on how to set the cardinality in configuration see ({{< ref "configuration-overview.md#metrics" >}}) + +This configuration is opt-in and is enabled via the Dapr configuration `spec.metrics.http.pathMatching`. When defined, it enables path matching, which standardizes specified paths for both metrics paths. This reduces the number of unique metrics paths, making metrics more manageable and reducing resource consumption in a controlled way. + +When `spec.metrics.http.pathMatching` is combined with the `increasedCardinality` flag set to `false`, non-matched paths are transformed into a catch-all bucket to control and limit cardinality, preventing unbounded path growth. Conversely, when `increasedCardinality` is `true` (the default), non-matched paths are passed through as they normally would be, allowing for potentially higher cardinality but preserving the original path data. + +### Examples of Path Matching in HTTP Metrics + +The following examples demonstrate how to use the Path Matching API in Dapr for managing HTTP metrics. On each example, the metrics are collected from 5 HTTP requests to the `/orders` endpoint with different order IDs. By adjusting cardinality and utilizing path matching, you can fine-tune metric granularity to balance detail and resource efficiency. + +These examples illustrate the cardinality of the metrics, highlighting that high cardinality configurations result in many entries, which correspond to higher memory usage for handling metrics. For simplicity, the following example focuses on a single metric: `dapr_http_server_request_count`. + +#### Low cardinality with path matching (Recommendation) + +Configuration: +```yaml +http: + increasedCardinality: false + pathMatching: + - /orders/{orderID} +``` + +Metrics generated: +``` +# matched paths +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/{orderID}",status="200"} 5 +# unmatched paths +dapr_http_server_request_count{app_id="order-service",method="GET",path="",status="200"} 1 +``` + +With low cardinality and path matching configured, you get the best of both worlds by grouping the metrics for the important endpoints without compromising the cardinality. This approach helps avoid high memory usage and potential security issues. + +#### Low cardinality without path matching + +Configuration: + +```yaml +http: + increasedCardinality: false +``` +Metrics generated: +``` +dapr_http_server_request_count{app_id="order-service",method="GET", path="",status="200"} 5 +``` + +In low cardinality mode, the path, which is the main source of unbounded cardinality, is dropped. This results in metrics that primarily indicate the number of requests made to the service for a given HTTP method, but without any information about the paths invoked. + + +#### High cardinality with path matching + +Configuration: +```yaml +http: + increasedCardinality: true + pathMatching: + - /orders/{orderID} +``` + +Metrics generated: +``` +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/{orderID}",status="200"} 5 +``` + +This example results from the same HTTP requests as the example above, but with path matching configured for the path `/orders/{orderID}`. By using path matching, you achieve reduced cardinality by grouping the metrics based on the matched path. + +#### High Cardinality without path matching + +Configuration: +```yaml +http: + increasedCardinality: true +``` + +Metrics generated: +``` +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/1",status="200"} 1 +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/2",status="200"} 1 +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/3",status="200"} 1 +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/4",status="200"} 1 +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders/5",status="200"} 1 +``` + +For each request, a new metric is created with the request path. This process continues for every request made to a new order ID, resulting in unbounded cardinality since the IDs are ever-growing. + + +### HTTP metrics exclude verbs + +The `excludeVerbs` option allows you to exclude specific HTTP verbs from being reported in the metrics. This can be useful in high-performance applications where memory savings are critical. + +### Examples of excluding HTTP verbs in metrics + +The following examples demonstrate how to exclude HTTP verbs in Dapr for managing HTTP metrics. + +#### Default - Include HTTP verbs + +Configuration: +```yaml +http: + excludeVerbs: false +``` + +Metrics generated: +``` +dapr_http_server_request_count{app_id="order-service",method="GET",path="/orders",status="200"} 1 +dapr_http_server_request_count{app_id="order-service",method="POST",path="/orders",status="200"} 1 +``` + +In this example, the HTTP method is included in the metrics, resulting in a separate metric for each request to the `/orders` endpoint. + +#### Exclude HTTP verbs + +Configuration: +```yaml +http: + excludeVerbs: true +``` + +Metrics generated: +``` +dapr_http_server_request_count{app_id="order-service",method="",path="/orders",status="200"} 2 +``` + +In this example, the HTTP method is excluded from the metrics, resulting in a single metric for all requests to the `/orders` endpoint. ## Configuring custom latency histogram buckets @@ -160,4 +282,4 @@ Using regular expressions to reduce metrics cardinality is considered legacy. We ## References * [Howto: Run Prometheus locally]({{< ref prometheus.md >}}) -* [Howto: Set up Prometheus and Grafana for metrics]({{< ref grafana.md >}}) +* [Howto: Set up Prometheus and Grafana for metrics]({{< ref grafana.md >}}) \ No newline at end of file From 2c69f666ee77d70a096041d13750e20faa933cbc Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 10:18:07 -0400 Subject: [PATCH 41/92] update config schema Signed-off-by: Hannah Hunter --- .../en/reference/resource-specs/configuration-schema.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daprdocs/content/en/reference/resource-specs/configuration-schema.md b/daprdocs/content/en/reference/resource-specs/configuration-schema.md index 5f267db11..b52228c16 100644 --- a/daprdocs/content/en/reference/resource-specs/configuration-schema.md +++ b/daprdocs/content/en/reference/resource-specs/configuration-schema.md @@ -41,6 +41,10 @@ spec: - http: increasedCardinality: + pathMatching: + - + - + excludeVerbs: httpPipeline: # for incoming http calls handlers: - name: From d77234b7bd70a313fad413f3b072fe8f89e8bbe9 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 10:30:06 -0400 Subject: [PATCH 42/92] go sdk Signed-off-by: Hannah Hunter --- sdkdocs/go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdkdocs/go b/sdkdocs/go index 5ef7aa223..7c03c7ce5 160000 --- a/sdkdocs/go +++ b/sdkdocs/go @@ -1 +1 @@ -Subproject commit 5ef7aa2234d4d4c07769ad31cde223ef11c4e33e +Subproject commit 7c03c7ce58d100a559ac1881bc0c80d6dedc5ab9 From 320bd37a4afdf88584e41c50a080b6c36d698faa Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 10:35:50 -0400 Subject: [PATCH 43/92] update submodules Signed-off-by: Hannah Hunter --- sdkdocs/dotnet | 2 +- sdkdocs/java | 2 +- sdkdocs/js | 2 +- sdkdocs/python | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdkdocs/dotnet b/sdkdocs/dotnet index c07eb698a..3768a983b 160000 --- a/sdkdocs/dotnet +++ b/sdkdocs/dotnet @@ -1 +1 @@ -Subproject commit c07eb698ac5d1b152a60d76c64af4841ffa07397 +Subproject commit 3768a983b7de973e22c7f070c315e13c955b6e07 diff --git a/sdkdocs/java b/sdkdocs/java index 2f5947392..a98327e7d 160000 --- a/sdkdocs/java +++ b/sdkdocs/java @@ -1 +1 @@ -Subproject commit 2f5947392a33bc7911e6669601ddb9e8b59b58fe +Subproject commit a98327e7d9a81611b0d7e91e59ea23ad48271948 diff --git a/sdkdocs/js b/sdkdocs/js index 4189a3d2a..7350742b6 160000 --- a/sdkdocs/js +++ b/sdkdocs/js @@ -1 +1 @@ -Subproject commit 4189a3d2ad6897406abd766f4ccbf2300c8f8852 +Subproject commit 7350742b6869cc166633d1f4d17d76fbdbb12921 diff --git a/sdkdocs/python b/sdkdocs/python index 0b7aafdab..64a4f2f66 160000 --- a/sdkdocs/python +++ b/sdkdocs/python @@ -1 +1 @@ -Subproject commit 0b7aafdab1d4fade424b1b6c9569329ad10bb516 +Subproject commit 64a4f2f6658e9023e8ea080eefdb019645cae802 From 77550b0a67521873271287967375243de7d4b725 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 10:38:06 -0400 Subject: [PATCH 44/92] forgot rust Signed-off-by: Hannah Hunter --- sdkdocs/rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdkdocs/rust b/sdkdocs/rust index ed283c2e2..4abf5aa65 160000 --- a/sdkdocs/rust +++ b/sdkdocs/rust @@ -1 +1 @@ -Subproject commit ed283c2e259c21cc77a24b3dbc03733103455f1b +Subproject commit 4abf5aa6504f7c0b0018d20f8dc038a486a67e3a From bc622a04a806fc04c01cebb1041406476569226d Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 12:29:14 -0400 Subject: [PATCH 45/92] fix error in dotnet-sdk Signed-off-by: Hannah Hunter --- sdkdocs/dotnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdkdocs/dotnet b/sdkdocs/dotnet index 3768a983b..56367963f 160000 --- a/sdkdocs/dotnet +++ b/sdkdocs/dotnet @@ -1 +1 @@ -Subproject commit 3768a983b7de973e22c7f070c315e13c955b6e07 +Subproject commit 56367963f46257fbcb109f671ac78dc445435012 From 205698fec22fbaa7540d1a466d2c4d4968c3a591 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 23 Jul 2024 15:24:33 -0400 Subject: [PATCH 46/92] jobs quickstart Signed-off-by: Hannah Hunter --- .../quickstarts/jobs-quickstart.md | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md new file mode 100644 index 000000000..c39ef56e2 --- /dev/null +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -0,0 +1,223 @@ +--- +type: docs +title: "Quickstart: Jobs" +linkTitle: Jobs +weight: 80 +description: Get started with the Dapr jobs building block +--- + +{{% alert title="Alpha" color="warning" %}} +The jobs building block is currently in **alpha**. +{{% /alert %}} + +Let's take a look at the [Dapr jobs building block]({{< ref jobs-overview.md >}}), which schedules and runs jobs at a specific time or interval. In this Quickstart, you'll schedule, get, and delete a job using Dapr's Job API. + +Need diagram + + +You can try out this pub/sub quickstart by either: + +- [Running all applications in this sample simultaneously with the Multi-App Run template file]({{< ref "#run-using-multi-app-run" >}}), or +- [Running one application at a time]({{< ref "#run-one-job-application-at-a-time" >}}) + + +## Run using Multi-App Run + +Select your preferred language-specific Dapr SDK before proceeding with the Quickstart. Currently, you can experiment with the jobs API with the Go SDK. + +{{< tabs Go >}} + + +{{% codetab %}} + +This quickstart includes two apps: + +- **`job-scheduler.go`:** schedules, retrieves, and deletes jobs. +- **`job-service.go`:** handles the scheduled jobs. + +### Step 1: Pre-requisites + +For this example, you will need: + +- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). +- [Latest version of Go](https://go.dev/dl/). + +- [Docker Desktop](https://www.docker.com/products/docker-desktop) + + +### Step 2: Set up the environment + +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/jobs). + +```bash +git clone https://github.com/dapr/quickstarts.git +``` + +From the root of the Quickstarts directory, navigate into the jobs directory: + +```bash +cd jobs/go/sdk +``` + +```bash +dapr run -f . +``` + +In the terminal output, you can see the following jobs being scheduled, retrieved, and deleted. + +- The `R2-D2` job is being scheduled. +- The `C-3PO` job is being scheduled. +- The `C-3PO` job is being retrieved. +- The `BB-8` job is being scheduled. +- The `BB-8` job is being retrieved. +- The `BB-8` job is being deleted. +- The `R2-D2` job is being executed after 5 seconds. +- The `R2-D2` job is being executed after 10 seconds. + +**Expected output** + +```text +== APP - job-service == dapr client initializing for: 127.0.0.1:6281 +== APP - job-service == Registered job handler for: R2-D2 +== APP - job-service == Registered job handler for: C-3PO +== APP - job-service == Registered job handler for: BB-8 +== APP - job-service == Starting server on port: 6200 +== APP - job-service == Job scheduled: R2-D2 +== APP - job-service == Job scheduled: C-3PO +== APP - job-service == 2024/07/17 18:09:59 job:{name:"C-3PO" due_time:"10s" data:{value:"{\"droid\":\"C-3PO\",\"Task\":\"Memory Wipe\"}"}} +== APP - job-scheduler == Get job response: {"droid":"C-3PO","Task":"Memory Wipe"} +== APP - job-service == Job scheduled: BB-8 +== APP - job-service == 2024/07/17 18:09:59 job:{name:"BB-8" due_time:"15s" data:{value:"{\"droid\":\"BB-8\",\"Task\":\"Internal Gyroscope Check\"}"}} +== APP - job-scheduler == Get job response: {"droid":"BB-8","Task":"Internal Gyroscope Check"} +== APP - job-scheduler == Deleted job: BB-8 +``` + +After 5 seconds, the terminal output should present the `R2-D2` job being processed: + +```text +== APP - job-service == Starting droid: R2-D2 +== APP - job-service == Executing maintenance job: Oil Change +``` + +After 10 seconds, the terminal output should present the `C3-PO` job being processed: + +```text +== APP - job-service == Starting droid: C-3PO +== APP - job-service == Executing maintenance job: Memory Wipe +``` + +Once the process has completed, you can stop and clean up application processes with a single command. + +```bash +dapr stop -f . +``` + +### What happened? + +{{% /codetab %}} + +{{< /tabs >}} + +## Run one job application at a time + +{{< tabs Go >}} + + +{{% codetab %}} + +This quickstart includes two apps: + +- **`job-scheduler.go`:** schedules, retrieves, and deletes jobs. +- **`job-service.go`:** handles the scheduled jobs. + +### Step 1: Pre-requisites + +For this example, you will need: + +- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). +- [Latest version of Go](https://go.dev/dl/). + +- [Docker Desktop](https://www.docker.com/products/docker-desktop) + + +### Step 2: Set up the environment + +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/jobs). + +```bash +git clone https://github.com/dapr/quickstarts.git +``` + +From the root of the Quickstarts directory, navigate into the jobs directory: + +```bash +cd jobs/go/sdk +``` + +### Step 3: Schedule jobs + +In the terminal, run the `job-service` app: + +```bash +dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 --dapr-grpc-port 6281 --app-protocol grpc -- go run . +``` + +**Expected output** + +```text +== APP == dapr client initializing for: 127.0.0.1:6281 +== APP == Registered job handler for: R2-D2 +== APP == Registered job handler for: C-3PO +== APP == Registered job handler for: BB-8 +== APP == Starting server on port: 6200 +``` + +In a new terminal window, run the `job-scheduler` app: + +```bash +dapr run --app-id job-scheduler --app-port 6300 -- go run . +``` + +**Expected output** + +```text +== APP == dapr client initializing for: +== APP == Get job response: {"droid":"C-3PO","Task":"Memory Wipe"} +== APP == Get job response: {"droid":"BB-8","Task":"Internal Gyroscope Check"} +== APP == Job deleted: BB-8 +``` + +Return to the `job-service` app terminal window. The output should be: + +```text +== APP == Job scheduled: R2-D2 +== APP == Job scheduled: C-3PO +== APP == 2024/07/17 18:25:36 job:{name:"C-3PO" due_time:"10s" data:{value:"{\"droid\":\"C-3PO\",\"Task\":\"Memory Wipe\"}"}} +== APP == Job scheduled: BB-8 +== APP == 2024/07/17 18:25:36 job:{name:"BB-8" due_time:"15s" data:{value:"{\"droid\":\"BB-8\",\"Task\":\"Internal Gyroscope Check\"}"}} +== APP == Starting droid: R2-D2 +== APP == Executing maintenance job: Oil Change +== APP == Starting droid: C-3PO +== APP == Executing maintenance job: Memory Wipe +``` + +### What happened? + + +{{% /codetab %}} + +{{< /tabs >}} + +## Tell us what you think! + +We're continuously working to improve our Quickstart examples and value your feedback. Did you find this Quickstart helpful? Do you have suggestions for improvement? + +Join the discussion in our [discord channel](https://discord.com/channels/778680217417809931/953427615916638238). + +## Next steps + +- Walk through [more examples of scheduling and orchestrating jobs using the jobs API](link) +- Learn more about [the jobs building block](link) +- Learn more about [the scheduler control plane](link) + +{{< button text="Explore Dapr tutorials >>" page="getting-started/tutorials/_index.md" >}} From f73909e48a0da4bbd785f68fdc366e18346923c9 Mon Sep 17 00:00:00 2001 From: Fernando Rocha Date: Wed, 24 Jul 2024 13:28:53 -0700 Subject: [PATCH 47/92] eks tutorial Signed-off-by: Fernando Rocha --- .../hosting/kubernetes/cluster/setup-eks.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md diff --git a/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md b/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md new file mode 100644 index 000000000..1d1d9c2c3 --- /dev/null +++ b/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md @@ -0,0 +1,47 @@ +--- +type: docs +title: "Set up an Elastic Kubernetes Service (EKS) cluster" +linkTitle: "Elastic Kubernetes Service (EKS)" +weight: 4000 +description: > + Learn how to set up an EKS Cluster +--- + +This guide walks you through installing an Elastic Kubernetes Service (EKS) cluster. If you need more information, refer to [Create an Amazon EKS cluster](https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html) + +## Prerequisites + +- Install: + - [Docker](https://docs.docker.com/install/) + - [kubectl](https://kubernetes.io/docs/tasks/tools/) + - [AWS CLI](https://aws.amazon.com/cli/) + - [eksctl](https://eksctl.io/) + +## Deploy an EKS cluster + +1. In the terminal, log into AWS. + + ```bash + aws configure + ``` + +1. Create an EKS cluster. To use a specific version of Kubernetes, use `--version` (1.13.x or newer version required). + + ```bash + eksctl create cluster --name [your_eks_cluster_name] --region [your_aws_region] --node-type t3.medium --nodes 3 --nodes-min 2 --nodes-max 4 --managed + ``` + +1. Configure kubectl + + ```bash + aws eks --region [your_aws_region] update-kubeconfig --name [your_eks_cluster_name] + ``` + +## Related links + +- [Learn more about EKS clusters](https://docs.aws.amazon.com/eks/latest/userguide/clusters.html) +- [Learn more about eksctl](https://eksctl.io/getting-started/) +- [Try out a Dapr quickstart]({{< ref quickstarts.md >}}) +- Learn how to [deploy Dapr on your cluster]({{< ref kubernetes-deploy.md >}}) +- [Upgrade Dapr on Kubernetes]({{< ref kubernetes-upgrade.md >}}) +- [Kubernetes production guidelines]({{< ref kubernetes-production.md >}}) From c8eef0fde023209c7efceef205099dd7a0861cd7 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 24 Jul 2024 16:28:54 -0400 Subject: [PATCH 48/92] update quickstart doc Signed-off-by: Hannah Hunter --- .../quickstarts/jobs-quickstart.md | 329 +++++++++++++++++- 1 file changed, 323 insertions(+), 6 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index c39ef56e2..36f132910 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -114,6 +114,318 @@ dapr stop -f . ### What happened? +When you ran `dapr init` during Dapr install: + +- The `dapr_scheduler` control plane was started alongside other Dapr services. +- [The `dapr.yaml` Multi-App Run template file]({{< ref #dapryaml-multi-app-run-template-filer >}}) was generated in the `.dapr/components` directory. + +Running `dapr run -f .` in this Quickstart started both the `job-scheduler` and the `job-service`. + +#### `dapr.yaml` Multi-App Run template file + +Running the [Multi-App Run template file]({{< ref multi-app-dapr-run >}}) with `dapr run -f .` starts all applications in your project. In this Quickstart, the `dapr.yaml` file contains the following: + +```yml +version: 1 +apps: + - appDirPath: ./job-service/ + appID: job-service + appPort: 6200 + daprGRPCPort: 6281 + appProtocol: grpc + command: ["go", "run", "."] + - appDirPath: ./job-scheduler/ + appID: job-scheduler + appPort: 6300 + command: ["go", "run", "."] +``` + +#### `job-service` app + +The `job-service` application creates handlers for the job service. + +```go +if err := server.AddServiceInvocationHandler("scheduleJob", scheduleJob); err != nil { + log.Fatalf("error adding invocation handler: %v", err) +} + +if err := server.AddServiceInvocationHandler("getJob", getJob); err != nil { + log.Fatalf("error adding invocation handler: %v", err) +} + +if err := server.AddServiceInvocationHandler("deleteJob", deleteJob); err != nil { + log.Fatalf("error adding invocation handler: %v", err) +} +``` + +Next, the jobs event handler is registered: + +```go +for _, jobName := range jobNames { + if err := server.AddJobEventHandler(jobName, handleJob); err != nil { + log.Fatalf("failed to register job event handler: %v", err) + } + fmt.Println("Registered job handler for: ", jobName) +} + +fmt.Println("Starting server on port: " + appPort) +if err = server.Start(); err != nil { + log.Fatalf("failed to start server: %v", err) +} +``` + +The `job-service` then codes the functions that handle scheduling, getting, and deleting jobs, and handles job events. + +```go +// Handler that schedules a DroidJob +func scheduleJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + + if in == nil { + err = errors.New("no invocation parameter") + return + } + + droidJob := DroidJob{} + err = json.Unmarshal(in.Data, &droidJob) + if err != nil { + fmt.Println("failed to unmarshal job: ", err) + return nil, err + } + + jobData := JobData{ + Droid: droidJob.Name, + Task: droidJob.Job, + } + + content, err := json.Marshal(jobData) + if err != nil { + fmt.Printf("Error marshalling job content") + return nil, err + } + + // schedule job + job := daprc.Job{ + Name: droidJob.Name, + DueTime: droidJob.DueTime, + Data: &anypb.Any{ + Value: content, + }, + } + + err = app.daprClient.ScheduleJobAlpha1(ctx, &job) + if err != nil { + fmt.Println("failed to schedule job. err: ", err) + return nil, err + } + + fmt.Println("Job scheduled: ", droidJob.Name) + + out = &common.Content{ + Data: in.Data, + ContentType: in.ContentType, + DataTypeURL: in.DataTypeURL, + } + + return out, err + +} + +// Handler that gets a job by name +func getJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + + if in == nil { + err = errors.New("no invocation parameter") + return nil, err + } + + job, err := app.daprClient.GetJobAlpha1(ctx, string(in.Data)) + if err != nil { + fmt.Println("failed to get job. err: ", err) + } + + out = &common.Content{ + Data: job.Data.Value, + ContentType: in.ContentType, + DataTypeURL: in.DataTypeURL, + } + + return out, err +} + +// Handler that deletes a job by name +func deleteJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + if in == nil { + err = errors.New("no invocation parameter") + return nil, err + } + + err = app.daprClient.DeleteJobAlpha1(ctx, string(in.Data)) + if err != nil { + fmt.Println("failed to delete job. err: ", err) + } + + out = &common.Content{ + Data: in.Data, + ContentType: in.ContentType, + DataTypeURL: in.DataTypeURL, + } + + return out, err +} + +// Handler that handles job events +func handleJob(ctx context.Context, job *common.JobEvent) error { + var jobData common.Job + if err := json.Unmarshal(job.Data, &jobData); err != nil { + return fmt.Errorf("failed to unmarshal job: %v", err) + } + decodedPayload, err := base64.StdEncoding.DecodeString(jobData.Value) + if err != nil { + return fmt.Errorf("failed to decode job payload: %v", err) + } + var jobPayload JobData + if err := json.Unmarshal(decodedPayload, &jobPayload); err != nil { + return fmt.Errorf("failed to unmarshal payload: %v", err) + } + + fmt.Println("Starting droid:", jobPayload.Droid) + fmt.Println("Executing maintenance job:", jobPayload.Task) + + return nil +} +``` + +#### `job-scheduler` app + +In the `job-scheduler` application, the R2D2, C3PO, and BB8 jobs are first defined as `[]DroidJob`: + +```go +droidJobs := []DroidJob{ + {Name: "R2-D2", Job: "Oil Change", DueTime: "5s"}, + {Name: "C-3PO", Job: "Memory Wipe", DueTime: "15s"}, + {Name: "BB-8", Job: "Internal Gyroscope Check", DueTime: "30s"}, +} +``` + + +The jobs are then scheduled, retrieved, and deleted using the jobs API. As you can see from the terminal output, first the R2D2 job is scheduled: + +```go +// Schedule R2D2 job +err = schedule(droidJobs[0]) +if err != nil { + log.Fatalln("Error scheduling job: ", err) +} + +time.Sleep(3 * time.Second) +``` + +Then, the C3PO job is scheduled, and returns job data: + +```go +// Schedule C-3PO job +err = schedule(droidJobs[1]) +if err != nil { + log.Fatalln("Error scheduling job: ", err) +} +time.Sleep(5 * time.Second) + +// Get C-3PO job +resp, err := get(droidJobs[1]) +if err != nil { + log.Fatalln("Error retrieving job: ", err) +} +fmt.Println("Get job response: ", resp) +``` + +The BB8 job is then scheduled, retrieved, and deleted: + +```go +// Schedule BB-8 job +err = schedule(droidJobs[2]) +if err != nil { + log.Fatalln("Error scheduling job: ", err) +} + +time.Sleep(5 * time.Second) + +// Get BB-8 job +resp, err = get(droidJobs[2]) +if err != nil { + log.Fatalln("Error retrieving job: ", err) +} +fmt.Println("Get job response: ", resp) + +time.Sleep(5 * time.Second) + +// Delete BB-8 job +err = delete(droidJobs[2]) +if err != nil { + log.Fatalln("Error deleting job: ", err) +} +fmt.Println("Job deleted: ", droidJobs[2].Name) +``` + +The `job-scheduler.go` also defines the `schedule`, `get`, and `delete` functions, calling from `job-service.go`. + +```go +// Schedules a job by invoking grpc service from job-service passing a DroidJob as an argument +func schedule(droidJob DroidJob) error { + jobData, err := json.Marshal(droidJob) + if err != nil { + fmt.Println("Error marshalling job content") + return err + } + + content := &daprc.DataContent{ + ContentType: "application/json", + Data: []byte(jobData), + } + + // Schedule Job + _, err = app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "scheduleJob", "POST", content) + if err != nil { + fmt.Println("Error invoking method: ", err) + return err + } + + return nil +} + +// Gets a job by invoking grpc service from job-service passing a job name as an argument +func get(droidJob DroidJob) (string, error) { + content := &daprc.DataContent{ + ContentType: "text/plain", + Data: []byte(droidJob.Name), + } + + //get job + resp, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "getJob", "GET", content) + if err != nil { + fmt.Println("Error invoking method: ", err) + return "", err + } + + return string(resp), nil +} + +// Deletes a job by invoking grpc service from job-service passing a job name as an argument +func delete(droidJob DroidJob) error { + content := &daprc.DataContent{ + ContentType: "text/plain", + Data: []byte(droidJob.Name), + } + + _, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "deleteJob", "DELETE", content) + if err != nil { + fmt.Println("Error invoking method: ", err) + return err + } + + return nil +} +``` + {{% /codetab %}} {{< /tabs >}} @@ -201,13 +513,17 @@ Return to the `job-service` app terminal window. The output should be: == APP == Executing maintenance job: Memory Wipe ``` -### What happened? - - {{% /codetab %}} {{< /tabs >}} + +## Watch the demo + +See the jobs API in action using a Go HTTP example, recorded during the [Dapr Community Call #107(https://www.youtube.com/live/WHGOc7Ec_YQ?si=JlOlcJKkhRuhf5R1&t=849)]. + + + ## Tell us what you think! We're continuously working to improve our Quickstart examples and value your feedback. Did you find this Quickstart helpful? Do you have suggestions for improvement? @@ -216,8 +532,9 @@ Join the discussion in our [discord channel](https://discord.com/channels/778680 ## Next steps -- Walk through [more examples of scheduling and orchestrating jobs using the jobs API](link) -- Learn more about [the jobs building block](link) -- Learn more about [the scheduler control plane](link) +- HTTP samples of this quickstart: + - [Go](todo) +- Learn more about [the jobs building block]({{< ref jobs-overview.md >}}) +- Learn more about [the scheduler control plane]({{< ref scheduler.md >}}) {{< button text="Explore Dapr tutorials >>" page="getting-started/tutorials/_index.md" >}} From 223f56e9a1b191b0993ba75d87d77e850e517248 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 24 Jul 2024 16:30:04 -0400 Subject: [PATCH 49/92] fix link Signed-off-by: Hannah Hunter --- .../content/en/getting-started/quickstarts/jobs-quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index 36f132910..4dfcbe12d 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -117,7 +117,7 @@ dapr stop -f . When you ran `dapr init` during Dapr install: - The `dapr_scheduler` control plane was started alongside other Dapr services. -- [The `dapr.yaml` Multi-App Run template file]({{< ref #dapryaml-multi-app-run-template-filer >}}) was generated in the `.dapr/components` directory. +- [The `dapr.yaml` Multi-App Run template file]({{< ref "#dapryaml-multi-app-run-template-file" >}}) was generated in the `.dapr/components` directory. Running `dapr run -f .` in this Quickstart started both the `job-scheduler` and the `job-service`. From cfcca3942ba05ef97f35fcc86505024afe340bc7 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 24 Jul 2024 16:33:02 -0400 Subject: [PATCH 50/92] edits Signed-off-by: Hannah Hunter --- .../quickstarts/jobs-quickstart.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index 4dfcbe12d..17a6409b6 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -59,21 +59,12 @@ From the root of the Quickstarts directory, navigate into the jobs directory: cd jobs/go/sdk ``` +Run the quickstart with one command: + ```bash dapr run -f . ``` -In the terminal output, you can see the following jobs being scheduled, retrieved, and deleted. - -- The `R2-D2` job is being scheduled. -- The `C-3PO` job is being scheduled. -- The `C-3PO` job is being retrieved. -- The `BB-8` job is being scheduled. -- The `BB-8` job is being retrieved. -- The `BB-8` job is being deleted. -- The `R2-D2` job is being executed after 5 seconds. -- The `R2-D2` job is being executed after 10 seconds. - **Expected output** ```text @@ -119,7 +110,16 @@ When you ran `dapr init` during Dapr install: - The `dapr_scheduler` control plane was started alongside other Dapr services. - [The `dapr.yaml` Multi-App Run template file]({{< ref "#dapryaml-multi-app-run-template-file" >}}) was generated in the `.dapr/components` directory. -Running `dapr run -f .` in this Quickstart started both the `job-scheduler` and the `job-service`. +Running `dapr run -f .` in this Quickstart started both the `job-scheduler` and the `job-service`. In the terminal output, you can see the following jobs being scheduled, retrieved, and deleted. + +- The `R2-D2` job is being scheduled. +- The `C-3PO` job is being scheduled. +- The `C-3PO` job is being retrieved. +- The `BB-8` job is being scheduled. +- The `BB-8` job is being retrieved. +- The `BB-8` job is being deleted. +- The `R2-D2` job is being executed after 5 seconds. +- The `R2-D2` job is being executed after 10 seconds. #### `dapr.yaml` Multi-App Run template file From ee5407122af9af88b1af58983a4cb2203bbce85b Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 24 Jul 2024 16:36:49 -0400 Subject: [PATCH 51/92] header Signed-off-by: Hannah Hunter --- .../content/en/getting-started/quickstarts/jobs-quickstart.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index 17a6409b6..d9c1cb3c4 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -59,7 +59,9 @@ From the root of the Quickstarts directory, navigate into the jobs directory: cd jobs/go/sdk ``` -Run the quickstart with one command: +### Step 3: Schedule jobs + +Run the application and schedule jobs with one command: ```bash dapr run -f . From 95cd31bc4848923b66c0bad33bf5c3a321fc9e5a Mon Sep 17 00:00:00 2001 From: Fernando Rocha Date: Wed, 24 Jul 2024 21:58:38 -0700 Subject: [PATCH 52/92] eks tutorial Signed-off-by: Fernando Rocha --- .../hosting/kubernetes/cluster/setup-eks.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md b/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md index 1d1d9c2c3..29813cbc6 100644 --- a/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md +++ b/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md @@ -16,6 +16,7 @@ This guide walks you through installing an Elastic Kubernetes Service (EKS) clus - [kubectl](https://kubernetes.io/docs/tasks/tools/) - [AWS CLI](https://aws.amazon.com/cli/) - [eksctl](https://eksctl.io/) + - [An existing VPC and subnets](https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html) ## Deploy an EKS cluster @@ -27,11 +28,19 @@ This guide walks you through installing an Elastic Kubernetes Service (EKS) clus 1. Create an EKS cluster. To use a specific version of Kubernetes, use `--version` (1.13.x or newer version required). +Change the values for vpc-private-subnets to meet your requirements. You can also add additional IDs. You must specify at least two subnet IDs. If you'd rather specify public subnets, you can change --vpc-private-subnets to --vpc-public-subnets. + ```bash - eksctl create cluster --name [your_eks_cluster_name] --region [your_aws_region] --node-type t3.medium --nodes 3 --nodes-min 2 --nodes-max 4 --managed + eksctl create cluster --name [your_eks_cluster_name] --region [your_aws_region] --version [kubernetes_version] --vpc-private-subnets [subnet_list_seprated_by_comma] --without-nodegroup ``` -1. Configure kubectl +1. Verify kubectl context: + + ```bash + kubectl config current-context + ``` + +1. If required, configured kubectl: ```bash aws eks --region [your_aws_region] update-kubeconfig --name [your_eks_cluster_name] From 19c7ca940ba88bc20b09dc3c412c498b7d6fab0e Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Thu, 25 Jul 2024 13:30:11 -0500 Subject: [PATCH 53/92] tweak howto-schedule-jobs && add howto-handle-triggered-jobs Signed-off-by: Cassandra Coyle --- .../jobs/howto-handle-triggered-jobs.md | 210 ++++++++++++++++++ .../jobs/howto-schedule-jobs.md | 86 +++++-- 2 files changed, 282 insertions(+), 14 deletions(-) create mode 100644 daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md new file mode 100644 index 000000000..9b88b237e --- /dev/null +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md @@ -0,0 +1,210 @@ +--- +type: docs +title: "How-To: Handle triggered jobs" +linkTitle: "How-To: Handle triggered jobs" +weight: 2000 +description: "Learn how to use the jobs API to schedule jobs and handle triggered jobs" +--- + +Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. The code examples below describe an application that schedules jobs for droid maintenance and another application to handle the triggered jobs that are sent back to the app at their dueTime. + + + +## Start the Scheduler service + +When you [run `dapr init` in either self-hosted mode or on Kubernetes]({{< ref install-dapr-selfhost.md >}}), the Dapr Scheduler service is started. + +## Set up the Jobs API + +In your code, set up and schedule jobs within your application. + +{{< tabs ".NET" "Go" >}} + +{{% codetab %}} + + + + +{{% /codetab %}} + +{{% codetab %}} + + + +The Go SDK schedules the job named `R2-D2`. For example, the following is application code to schedule jobs. + +```go +package main + +import ( + "fmt" + "io" + "log" + "net/http" + "os" + "strings" + "time" +) + +var r2d2JobBody = `{ + "data": { + "@type": "type.googleapis.com/google.protobuf.StringValue", + "value": "R2-D2:Oil Change" + }, + "dueTime": "2s" + }` + +func main() { + //Sleep for 5 seconds to wait for job-service to start + time.Sleep(5 * time.Second) + + daprHost := os.Getenv("DAPR_HOST") + if daprHost == "" { + daprHost = "http://localhost" + } + + schedulerDaprHttpPort := "6280" + + client := http.Client{ + Timeout: 30 * time.Second, + } + + // Schedule a job using the Dapr Jobs API with short dueTime + jobName := "R2-D2" + reqURL := daprHost + ":" + schedulerDaprHttpPort + "/v1.0-alpha1/jobs/" + jobName + + req, err := http.NewRequest("POST", reqURL, strings.NewReader(r2d2JobBody)) + if err != nil { + log.Fatal(err.Error()) + } + + req.Header.Set("Content-Type", "application/json") + + // Schedule a job using the Dapr Jobs API + res, err := client.Do(req) + if err != nil { + log.Fatal(err) + } + + if res.StatusCode != http.StatusNoContent { + log.Fatalf("failed to register job event handler. status code: %v", res.StatusCode) + } + + defer res.Body.Close() + + fmt.Println("Job Scheduled:", jobName) + + // ... +} +``` + +The following is application code to handle the triggered jobs that are sent back to the application at their dueTime. + +```go +package main + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "strings" +) + + +type Job struct { + TypeURL string `json:"type_url"` + Value string `json:"value"` +} + +type DroidJob struct { + Droid string `json:"droid"` + Task string `json:"task"` +} + +func main() { + appPort := os.Getenv("APP_PORT") + if appPort == "" { + appPort = "6200" + } + + // Setup job handler + http.HandleFunc("/job/", handleJob) + + fmt.Printf("Server started on port %v\n", appPort) + err := http.ListenAndServe(":"+appPort, nil) + if err != nil { + log.Fatal(err) + } + +} + +func handleJob(w http.ResponseWriter, r *http.Request) { + fmt.Println("Received job request...") + rawBody, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, fmt.Sprintf("error reading request body: %v", err), http.StatusBadRequest) + return + } + + var jobData Job + if err := json.Unmarshal(rawBody, &jobData); err != nil { + http.Error(w, fmt.Sprintf("error decoding JSON: %v", err), http.StatusBadRequest) + return + } + + // Decoding job data + decodedValue, err := base64.RawStdEncoding.DecodeString(jobData.Value) + if err != nil { + fmt.Printf("Error decoding base64: %v", err) + http.Error(w, fmt.Sprintf("error decoding base64: %v", err), http.StatusBadRequest) + return + } + + // Creating Droid Job from decoded value + droidJob := setDroidJob(string(decodedValue)) + + fmt.Println("Starting droid:", droidJob.Droid) + fmt.Println("Executing maintenance job:", droidJob.Task) + + w.WriteHeader(http.StatusOK) +} + +func setDroidJob(decodedValue string) DroidJob { + // Removing new lines from decoded value - Workaround for base64 encoding issue + droidStr := strings.ReplaceAll(decodedValue, "\n", "") + droidArray := strings.Split(droidStr, ":") + + droidJob := DroidJob{Droid: droidArray[0], Task: droidArray[1]} + return droidJob +} +``` + +{{% /codetab %}} + + +{{< /tabs >}} + +## Run the Dapr sidecar + +Once you've set up the Jobs API in your application, run the Dapr sidecar. + +```bash +// service to handle the triggered jobs +// run locally to the directory where the job handler service lives +dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- go run . + +// service to schedule a job to be sent back at some point in the future +// run locally to the directory where the job scheduler service lives +dapr run --app-id job-scheduler --app-port 6300 --dapr-http-port 6380 -- go run . +``` + +## Next steps + +- [Learn more about the Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) +- [Jobs API reference]({{< ref jobs_api.md >}}) \ No newline at end of file diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md index b3dda8553..5490b1a3c 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md @@ -6,7 +6,7 @@ weight: 2000 description: "Learn how to use the jobs API to schedule jobs" --- -Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. The code example below describes an application that schedules jobs for a **TBD** application. +Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. The code example below describes an application that schedules jobs for a database backup application. -The Go SDK triggers the job called `daprc.Job` to schedule jobs. Job data is housed in a backup database (`"my-prod-db"`) and are called with `ScheduleJobAlpha1`. For example: +The Go SDK schedules the job named `prod-db-backup`. Job data is housed in a backup database (`"my-prod-db"`) and are called with `ScheduleJobAlpha1`. For example: ```go package main @@ -52,8 +52,24 @@ func main() { // Initialize the server server, err := daprs.NewService(":50070") - // ... + if err != nil { + log.Fatalf("failed to start the server: %v", err) + } + if err = server.AddJobEventHandler("prod-db-backup", prodDBBackupHandler); err != nil { + log.Fatalf("failed to register job event handler: %v", err) + } + + log.Println("starting server") + go func() { + if err = server.Start(); err != nil { + log.Fatalf("failed to start server: %v", err) + } + }() + + // Brief intermission to allow for the server to initialize. + time.Sleep(10 * time.Second) + ctx := context.Background() // Set up backup location @@ -65,10 +81,12 @@ func main() { }, }, ) - - // ... - // Set up the job data + if err != nil { + panic(err) + } + + // Set up the job job := daprc.Job{ Name: "prod-db-backup", Schedule: "@every 1s", @@ -80,23 +98,63 @@ func main() { // Create the client client, err := daprc.NewClient() - //... + if err != nil { + panic(err) + } + defer client.Close() // Schedule job err = client.ScheduleJobAlpha1(ctx, &job) + if err != nil { + panic(err) + } - // ... - // Get job + fmt.Println("schedulejob - success") + + time.Sleep(3 * time.Second) + + // Get job resp, err := client.GetJobAlpha1(ctx, "prod-db-backup") - // ... - + if err != nil { + panic(err) + } + fmt.Printf("getjob - resp: %v\n", resp) // parse + // Delete job err = client.DeleteJobAlpha1(ctx, "prod-db-backup") - // .. + if err != nil { + fmt.Printf("job deletion error: %v\n", err) + } else { + fmt.Println("deletejob - success") + } + + if err = server.Stop(); err != nil { + log.Fatalf("failed to stop server: %v\n", err) + } } var jobCount = 0 + + +func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error { + var jobData common.Job + if err := json.Unmarshal(job.Data, &jobData); err != nil { + return fmt.Errorf("failed to unmarshal job: %v", err) + } + decodedPayload, err := base64.StdEncoding.DecodeString(jobData.Value) + if err != nil { + return fmt.Errorf("failed to decode job payload: %v", err) + } + var jobPayload api.DBBackup + if err := json.Unmarshal(decodedPayload, &jobPayload); err != nil { + return fmt.Errorf("failed to unmarshal payload: %v", err) + } + fmt.Printf("job %d received:\n type: %v \n typeurl: %v\n value: %v\n extracted payload: %v\n", jobCount, job.JobType, jobData.TypeURL, jobData.Value, jobPayload) + jobCount++ + return nil +} + ``` {{% /codetab %}} @@ -109,7 +167,7 @@ var jobCount = 0 Once you've set up the Jobs API in your application, run the Dapr sidecar. ```bash -dapr run --app-id=distributed-scheduler --metrics-port=9091 --scheduler-host-address=localhost:50006 --dapr-grpc-port 50001 --app-port 50070 --app-protocol grpc --log-level debug go run ./main.go +dapr run --app-id=distributed-scheduler --metrics-port=9091 --dapr-grpc-port 50001 --app-port 50070 --app-protocol grpc --log-level debug go run ./main.go ``` ## Next steps From 38cdb1a45157d879207cb840c63fa1f08b7b3de7 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Thu, 25 Jul 2024 15:51:06 -0400 Subject: [PATCH 54/92] review cassies additions Signed-off-by: Hannah Hunter --- .../jobs/howto-handle-triggered-jobs.md | 37 +++++++++++++------ .../jobs/howto-schedule-jobs.md | 9 +---- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md index 9b88b237e..fb978ac20 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md @@ -6,7 +6,9 @@ weight: 2000 description: "Learn how to use the jobs API to schedule jobs and handle triggered jobs" --- -Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. The code examples below describe an application that schedules jobs for droid maintenance and another application to handle the triggered jobs that are sent back to the app at their dueTime. +Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. This guide follows an example that uses two applications to set up the jobs API: +- `job-scheduler` application: schedules jobs for droid maintenance. +- `job-service` application: handles the triggered jobs that are sent back to the app at their `dueTime`. - - -{{% /codetab %}} +{{< tabs "Go" >}} {{% codetab %}} -The Go SDK schedules the job named `R2-D2`. For example, the following is application code to schedule jobs. +For example, the following `job-scheduler` application code uses Go HTTP client to schedule a job named `R2-D2` with a `"value"` of `"R2-D2:Oil Change"` and a `dueTime` of `"2s"`. ```go package main @@ -100,7 +97,11 @@ func main() { } ``` -The following is application code to handle the triggered jobs that are sent back to the application at their dueTime. +### Create a job handler + +Next, in a second application, you need to create a job handler that handles the triggered jobs that are sent back to the application. + +For example, the following `job-service` application code has a job handler called `handleJob` to deal with the triggered R2D2 job from earlier, at its `"dueTime": "2s"`. ```go package main @@ -192,18 +193,30 @@ func setDroidJob(decodedValue string) DroidJob { ## Run the Dapr sidecar -Once you've set up the Jobs API in your application, run the Dapr sidecar. +Once you've set up the Jobs API in your application, run the Dapr sidecar. In a terminal window, run the following Dapr command for the `job-service` application. ```bash // service to handle the triggered jobs // run locally to the directory where the job handler service lives dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- go run . +``` +In a second terminal window, run the following Dapr command to start the `job-scheduler` application. + +```bash // service to schedule a job to be sent back at some point in the future // run locally to the directory where the job scheduler service lives dapr run --app-id job-scheduler --app-port 6300 --dapr-http-port 6380 -- go run . ``` +**Expected output** + +```text + +``` + + + ## Next steps - [Learn more about the Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md index 5490b1a3c..be297dcd9 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md @@ -20,14 +20,7 @@ When you [run `dapr init` in either self-hosted mode or on Kubernetes]({{< ref i In your code, set up and schedule jobs within your application. -{{< tabs ".NET" "Go" >}} - -{{% codetab %}} - - - - -{{% /codetab %}} +{{< tabs "Go" >}} {{% codetab %}} From 81b377074aac8a682b495a17ea48ed9f9f8035db Mon Sep 17 00:00:00 2001 From: Fernando Rocha Date: Thu, 25 Jul 2024 12:53:15 -0700 Subject: [PATCH 55/92] adding information about inbound port Signed-off-by: Fernando Rocha --- .../hosting/kubernetes/cluster/setup-eks.md | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md b/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md index 29813cbc6..b6e9b7747 100644 --- a/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md +++ b/daprdocs/content/en/operations/hosting/kubernetes/cluster/setup-eks.md @@ -12,7 +12,6 @@ This guide walks you through installing an Elastic Kubernetes Service (EKS) clus ## Prerequisites - Install: - - [Docker](https://docs.docker.com/install/) - [kubectl](https://kubernetes.io/docs/tasks/tools/) - [AWS CLI](https://aws.amazon.com/cli/) - [eksctl](https://eksctl.io/) @@ -28,29 +27,42 @@ This guide walks you through installing an Elastic Kubernetes Service (EKS) clus 1. Create an EKS cluster. To use a specific version of Kubernetes, use `--version` (1.13.x or newer version required). -Change the values for vpc-private-subnets to meet your requirements. You can also add additional IDs. You must specify at least two subnet IDs. If you'd rather specify public subnets, you can change --vpc-private-subnets to --vpc-public-subnets. - ```bash eksctl create cluster --name [your_eks_cluster_name] --region [your_aws_region] --version [kubernetes_version] --vpc-private-subnets [subnet_list_seprated_by_comma] --without-nodegroup ``` + Change the values for `vpc-private-subnets` to meet your requirements. You can also add additional IDs. You must specify at least two subnet IDs. If you'd rather specify public subnets, you can change `--vpc-private-subnets` to `--vpc-public-subnets`. + 1. Verify kubectl context: ```bash kubectl config current-context ``` -1. If required, configured kubectl: +1. Update the security group rule to allow the EKS cluster to communicate with the Dapr Sidecar by creating an inbound rule for port 4000. ```bash - aws eks --region [your_aws_region] update-kubeconfig --name [your_eks_cluster_name] + aws ec2 authorize-security-group-ingress --region [your_aws_region] \ + --group-id [your_security_group] \ + --protocol tcp \ + --port 4000 \ + --source-group [your_security_group] ``` +## Troubleshooting + +### Access permissions + +If you face any access permissions, make sure you are using the same AWS profile that was used to create the cluster. If needed, update the kubectl configuration with the correct profile: + +```bash +aws eks --region [your_aws_region] update-kubeconfig --name [your_eks_cluster_name] --profile [your_profile_name] +``` + ## Related links - [Learn more about EKS clusters](https://docs.aws.amazon.com/eks/latest/userguide/clusters.html) - [Learn more about eksctl](https://eksctl.io/getting-started/) - [Try out a Dapr quickstart]({{< ref quickstarts.md >}}) - Learn how to [deploy Dapr on your cluster]({{< ref kubernetes-deploy.md >}}) -- [Upgrade Dapr on Kubernetes]({{< ref kubernetes-upgrade.md >}}) - [Kubernetes production guidelines]({{< ref kubernetes-production.md >}}) From c8fd729dc3d0b7090c411d01de0c919e75e36014 Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Fri, 26 Jul 2024 10:04:09 -0500 Subject: [PATCH 56/92] rm separate howto and add the handling of triggered jobs to the initial example Signed-off-by: Cassandra Coyle --- .../jobs/howto-handle-triggered-jobs.md | 100 +----------------- ...wto-schedule-and-handle-triggered-jobs.md} | 83 ++++++--------- 2 files changed, 37 insertions(+), 146 deletions(-) rename daprdocs/content/en/developing-applications/building-blocks/jobs/{howto-schedule-jobs.md => howto-schedule-and-handle-triggered-jobs.md} (58%) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md index fb978ac20..5bb4393f0 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md @@ -1,83 +1,3 @@ ---- -type: docs -title: "How-To: Handle triggered jobs" -linkTitle: "How-To: Handle triggered jobs" -weight: 2000 -description: "Learn how to use the jobs API to schedule jobs and handle triggered jobs" ---- - -Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. This guide follows an example that uses two applications to set up the jobs API: -- `job-scheduler` application: schedules jobs for droid maintenance. -- `job-service` application: handles the triggered jobs that are sent back to the app at their `dueTime`. - - - -## Start the Scheduler service - -When you [run `dapr init` in either self-hosted mode or on Kubernetes]({{< ref install-dapr-selfhost.md >}}), the Dapr Scheduler service is started. - -## Set up the Jobs API - -### Schedule jobs - -In your code, start by setting up and scheduling jobs your project. In the first application, create a new job, including its `name`, `data`, and `dueTime` values. - -{{< tabs "Go" >}} - -{{% codetab %}} - - - -For example, the following `job-scheduler` application code uses Go HTTP client to schedule a job named `R2-D2` with a `"value"` of `"R2-D2:Oil Change"` and a `dueTime` of `"2s"`. - -```go -package main - -import ( - "fmt" - "io" - "log" - "net/http" - "os" - "strings" - "time" -) - -var r2d2JobBody = `{ - "data": { - "@type": "type.googleapis.com/google.protobuf.StringValue", - "value": "R2-D2:Oil Change" - }, - "dueTime": "2s" - }` - -func main() { - //Sleep for 5 seconds to wait for job-service to start - time.Sleep(5 * time.Second) - - daprHost := os.Getenv("DAPR_HOST") - if daprHost == "" { - daprHost = "http://localhost" - } - - schedulerDaprHttpPort := "6280" - - client := http.Client{ - Timeout: 30 * time.Second, - } - - // Schedule a job using the Dapr Jobs API with short dueTime - jobName := "R2-D2" - reqURL := daprHost + ":" + schedulerDaprHttpPort + "/v1.0-alpha1/jobs/" + jobName - - req, err := http.NewRequest("POST", reqURL, strings.NewReader(r2d2JobBody)) - if err != nil { - log.Fatal(err.Error()) - } - - req.Header.Set("Content-Type", "application/json") // Schedule a job using the Dapr Jobs API res, err := client.Do(req) @@ -97,11 +17,7 @@ func main() { } ``` -### Create a job handler - -Next, in a second application, you need to create a job handler that handles the triggered jobs that are sent back to the application. - -For example, the following `job-service` application code has a job handler called `handleJob` to deal with the triggered R2D2 job from earlier, at its `"dueTime": "2s"`. +The following is application code to handle the triggered jobs that are sent back to the application at their dueTime. ```go package main @@ -193,30 +109,18 @@ func setDroidJob(decodedValue string) DroidJob { ## Run the Dapr sidecar -Once you've set up the Jobs API in your application, run the Dapr sidecar. In a terminal window, run the following Dapr command for the `job-service` application. +Once you've set up the Jobs API in your application, run the Dapr sidecar. ```bash // service to handle the triggered jobs // run locally to the directory where the job handler service lives dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- go run . -``` -In a second terminal window, run the following Dapr command to start the `job-scheduler` application. - -```bash // service to schedule a job to be sent back at some point in the future // run locally to the directory where the job scheduler service lives dapr run --app-id job-scheduler --app-port 6300 --dapr-http-port 6380 -- go run . ``` -**Expected output** - -```text - -``` - - - ## Next steps - [Learn more about the Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md similarity index 58% rename from daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md rename to daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md index be297dcd9..9d5740d27 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md @@ -1,12 +1,12 @@ --- type: docs -title: "How-To: Schedule jobs" -linkTitle: "How-To: Schedule jobs" +title: "How-To: Schedule and handle triggered jobs" +linkTitle: "How-To: Schedule and handle triggered jobs" weight: 2000 -description: "Learn how to use the jobs API to schedule jobs" +description: "Learn how to use the jobs API to schedule and handle triggered jobs" --- -Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. The code example below describes an application that schedules jobs for a database backup application. +Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. The code example below describes an application that schedules jobs for a database backup application and handles them at trigger time, also known as the time the job was sent back to the application because it reached it's dueTime. -The Go SDK schedules the job named `prod-db-backup`. Job data is housed in a backup database (`"my-prod-db"`) and are called with `ScheduleJobAlpha1`. For example: +The following Go SDK code sample schedules the job named `prod-db-backup`. Job data is housed in a backup database (`"my-prod-db"`) and is scheduled with `ScheduleJobAlpha1`, +providing the job `data` which is the backup `task` name and `metadata` including the location and database name. The job is scheduled with a `schedule` set and the amount of `repeats` +desired, meaning there is a max amount of times the job should be triggered and sent back to the app. At trigger time, so `@every 1s` according to the `schedule`, this job will be +triggered and sent back to the application up to the max repeats set which is `10`. At the trigger time, the `prodDBBackupHandler` function is called executing the +desired business logic for this job at trigger time. For example: ```go package main @@ -41,13 +45,9 @@ import ( ) func main() { - // Initialize the server server, err := daprs.NewService(":50070") - - if err != nil { - log.Fatalf("failed to start the server: %v", err) - } + // ... if err = server.AddJobEventHandler("prod-db-backup", prodDBBackupHandler); err != nil { log.Fatalf("failed to register job event handler: %v", err) @@ -59,11 +59,7 @@ func main() { log.Fatalf("failed to start server: %v", err) } }() - - // Brief intermission to allow for the server to initialize. - time.Sleep(10 * time.Second) - - ctx := context.Background() + // ... // Set up backup location jobData, err := json.Marshal(&api.DBBackup{ @@ -74,10 +70,7 @@ func main() { }, }, ) - - if err != nil { - panic(err) - } + // ... // Set up the job job := daprc.Job{ @@ -91,36 +84,25 @@ func main() { // Create the client client, err := daprc.NewClient() - if err != nil { - panic(err) - } + // ... + defer client.Close() // Schedule job err = client.ScheduleJobAlpha1(ctx, &job) - if err != nil { - panic(err) - } - - - fmt.Println("schedulejob - success") - - time.Sleep(3 * time.Second) + // ... + fmt.Println("schedule job - success") // Get job resp, err := client.GetJobAlpha1(ctx, "prod-db-backup") - if err != nil { - panic(err) - } - fmt.Printf("getjob - resp: %v\n", resp) // parse - + // ... + fmt.Printf("get job - resp: %v\n", resp) // parse + + // Let test run and then cleanup the job + time.Sleep(20 * time.Second) // Delete job err = client.DeleteJobAlpha1(ctx, "prod-db-backup") - if err != nil { - fmt.Printf("job deletion error: %v\n", err) - } else { - fmt.Println("deletejob - success") - } + // ... if err = server.Stop(); err != nil { log.Fatalf("failed to stop server: %v\n", err) @@ -129,19 +111,18 @@ func main() { var jobCount = 0 - +// at job trigger time this function is called func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error { var jobData common.Job if err := json.Unmarshal(job.Data, &jobData); err != nil { - return fmt.Errorf("failed to unmarshal job: %v", err) + // ... } decodedPayload, err := base64.StdEncoding.DecodeString(jobData.Value) - if err != nil { - return fmt.Errorf("failed to decode job payload: %v", err) - } + // ... + var jobPayload api.DBBackup if err := json.Unmarshal(decodedPayload, &jobPayload); err != nil { - return fmt.Errorf("failed to unmarshal payload: %v", err) + // ... } fmt.Printf("job %d received:\n type: %v \n typeurl: %v\n value: %v\n extracted payload: %v\n", jobCount, job.JobType, jobData.TypeURL, jobData.Value, jobPayload) jobCount++ @@ -157,10 +138,16 @@ func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error { ## Run the Dapr sidecar -Once you've set up the Jobs API in your application, run the Dapr sidecar. +Once you've set up the Jobs API in your application, in a terminal window run the Dapr sidecar with the following command. ```bash -dapr run --app-id=distributed-scheduler --metrics-port=9091 --dapr-grpc-port 50001 --app-port 50070 --app-protocol grpc --log-level debug go run ./main.go +dapr run --app-id=distributed-scheduler \ + --metrics-port=9091 \ + --dapr-grpc-port 50001 \ + --app-port 50070 \ + --app-protocol grpc \ + --log-level debug \ + go run ./main.go ``` ## Next steps From 6b8dcaf0823e9425a214056fe7c3a65c61223763 Mon Sep 17 00:00:00 2001 From: Cassandra Coyle Date: Fri, 26 Jul 2024 14:38:04 -0500 Subject: [PATCH 57/92] rm unneccessary howto Signed-off-by: Cassandra Coyle --- .../jobs/howto-handle-triggered-jobs.md | 127 ------------------ 1 file changed, 127 deletions(-) delete mode 100644 daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md deleted file mode 100644 index 5bb4393f0..000000000 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-handle-triggered-jobs.md +++ /dev/null @@ -1,127 +0,0 @@ - - // Schedule a job using the Dapr Jobs API - res, err := client.Do(req) - if err != nil { - log.Fatal(err) - } - - if res.StatusCode != http.StatusNoContent { - log.Fatalf("failed to register job event handler. status code: %v", res.StatusCode) - } - - defer res.Body.Close() - - fmt.Println("Job Scheduled:", jobName) - - // ... -} -``` - -The following is application code to handle the triggered jobs that are sent back to the application at their dueTime. - -```go -package main - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "io" - "log" - "net/http" - "os" - "strings" -) - - -type Job struct { - TypeURL string `json:"type_url"` - Value string `json:"value"` -} - -type DroidJob struct { - Droid string `json:"droid"` - Task string `json:"task"` -} - -func main() { - appPort := os.Getenv("APP_PORT") - if appPort == "" { - appPort = "6200" - } - - // Setup job handler - http.HandleFunc("/job/", handleJob) - - fmt.Printf("Server started on port %v\n", appPort) - err := http.ListenAndServe(":"+appPort, nil) - if err != nil { - log.Fatal(err) - } - -} - -func handleJob(w http.ResponseWriter, r *http.Request) { - fmt.Println("Received job request...") - rawBody, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, fmt.Sprintf("error reading request body: %v", err), http.StatusBadRequest) - return - } - - var jobData Job - if err := json.Unmarshal(rawBody, &jobData); err != nil { - http.Error(w, fmt.Sprintf("error decoding JSON: %v", err), http.StatusBadRequest) - return - } - - // Decoding job data - decodedValue, err := base64.RawStdEncoding.DecodeString(jobData.Value) - if err != nil { - fmt.Printf("Error decoding base64: %v", err) - http.Error(w, fmt.Sprintf("error decoding base64: %v", err), http.StatusBadRequest) - return - } - - // Creating Droid Job from decoded value - droidJob := setDroidJob(string(decodedValue)) - - fmt.Println("Starting droid:", droidJob.Droid) - fmt.Println("Executing maintenance job:", droidJob.Task) - - w.WriteHeader(http.StatusOK) -} - -func setDroidJob(decodedValue string) DroidJob { - // Removing new lines from decoded value - Workaround for base64 encoding issue - droidStr := strings.ReplaceAll(decodedValue, "\n", "") - droidArray := strings.Split(droidStr, ":") - - droidJob := DroidJob{Droid: droidArray[0], Task: droidArray[1]} - return droidJob -} -``` - -{{% /codetab %}} - - -{{< /tabs >}} - -## Run the Dapr sidecar - -Once you've set up the Jobs API in your application, run the Dapr sidecar. - -```bash -// service to handle the triggered jobs -// run locally to the directory where the job handler service lives -dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- go run . - -// service to schedule a job to be sent back at some point in the future -// run locally to the directory where the job scheduler service lives -dapr run --app-id job-scheduler --app-port 6300 --dapr-http-port 6380 -- go run . -``` - -## Next steps - -- [Learn more about the Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) -- [Jobs API reference]({{< ref jobs_api.md >}}) \ No newline at end of file From 2deca48e63e9dcef73ce8e10fdd21095ec2de31b Mon Sep 17 00:00:00 2001 From: Josh van Leeuwen Date: Fri, 26 Jul 2024 22:14:20 +0100 Subject: [PATCH 58/92] Updates API ref for jobs. Adds doc on Kubernetes Scheduler persistent (#4256) * Updates API ref for jobs. Adds doc on Kubernetes Scheduler persistent volume Signed-off-by: joshvanl * Apply suggestions from code review Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Josh van Leeuwen * Adds scheduler persistent volume docs for selfhosted Signed-off-by: joshvanl * Updates scheduler volume docs based on https://github.com/dapr/cli/pull/1423 Signed-off-by: joshvanl * Apply suggestions from code review Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Josh van Leeuwen * Adds reference for file system location of scheduler local volume Signed-off-by: joshvanl * Update daprdocs/content/en/reference/api/jobs_api.md Co-authored-by: Cassie Coyle Signed-off-by: Josh van Leeuwen * Update daprdocs/content/en/operations/hosting/self-hosted/self-hosted-persisting-scheduler.md Signed-off-by: Mark Fussell * Apply suggestions from code review Co-authored-by: Mark Fussell Signed-off-by: Josh van Leeuwen * Adds default volume name for scheduler dapr init Signed-off-by: joshvanl * Adds directions for getting scheduler volume from Docker Desktop Signed-off-by: joshvanl * Update daprdocs/content/en/operations/hosting/self-hosted/self-hosted-persisting-scheduler.md Signed-off-by: Mark Fussell --------- Signed-off-by: joshvanl Signed-off-by: Josh van Leeuwen Signed-off-by: Mark Fussell Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Co-authored-by: Cassie Coyle Co-authored-by: Mark Fussell --- .../jobs/howto-schedule-jobs.md | 19 ++-- .../building-blocks/jobs/jobs-overview.md | 19 ++-- .../kubernetes-persisting-scheduler.md | 55 ++++++++++++ .../self-hosted-persisting-scheduler.md | 27 ++++++ daprdocs/content/en/reference/api/jobs_api.md | 86 ++++++++++++++----- .../content/en/reference/cli/dapr-init.md | 5 +- 6 files changed, 172 insertions(+), 39 deletions(-) create mode 100644 daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md create mode 100644 daprdocs/content/en/operations/hosting/self-hosted/self-hosted-persisting-scheduler.md diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md index 166530635..fb4263ef1 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-jobs.md @@ -8,19 +8,22 @@ description: "Learn how to use the jobs API to schedule jobs" Now that you've learned what the [jobs building block]({{< ref jobs-overview.md >}}) provides, let's look at an example of how to use the API. The code example below describes an application that schedules jobs for a **TBD** application. - - - ## Set up the Scheduler service -When you run `dapr init` in either self-hosted mode or on Kubernetes, the Dapr scheduler service is started. +{{% alert title="Warning" color="warning" %}} +By default, job data is not resilient to [Scheduler]({{< ref scheduler.md >}}) service restarts. +A persistent volume must be provided to Scheduler to ensure job data is not lost in either [Kubernetes]({{< ref kubernetes-persisting-scheduler.md >}}) or [Self-Hosted]({{< ref self-hosted-persisting-scheduler.md >}}) mode. +{{% /alert %}} -## Run the Dapr sidecar +When you run `dapr init` in either self-hosted mode or on Kubernetes, the Dapr scheduler service is started. -Run the Dapr sidecar alongside your application. +## Run the Dapr sidecar + +Run the Dapr sidecar alongside your application. ```bash dapr run --app-id=jobs --app-port 50070 --app-protocol grpc --log-level debug -- go run main.go @@ -29,4 +32,4 @@ dapr run --app-id=jobs --app-port 50070 --app-protocol grpc --log-level debug -- ## Next steps - [Learn more about the Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) -- [Jobs API reference]({{< ref jobs_api.md >}}) \ No newline at end of file +- [Jobs API reference]({{< ref jobs_api.md >}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md index ffea762f2..a7838088d 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md @@ -8,14 +8,19 @@ description: "Overview of the jobs API building block" Many applications require job scheduling, or the need to take an action in the future. The jobs API is an orchestrator for scheduling these future jobs, either at a specific time or for a specific interval. -Not only does the jobs API help you with scheduling jobs, but internally, Dapr uses the scheduler service to schedule actor reminders. +Not only does the jobs API help you with scheduling jobs, but internally, Dapr uses the scheduler service to schedule actor reminders. Jobs in Dapr consist of: -- The jobs API building block +- [The jobs API building block]({{< ref jobs_api.md >}}) - [The Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) [See example scenarios.]({{< ref "#scenarios" >}}) +{{% alert title="Warning" color="warning" %}} +By default, job data is not resilient to [Scheduler]({{< ref scheduler.md >}}) service restarts. +A persistent volume must be provided to Scheduler to ensure job data is not lost in either [Kubernetes]({{< ref kubernetes-persisting-scheduler.md >}}) or [Self-hosted]({{< ref self-hosted-persisting-scheduler.md >}}) mode. +{{% /alert %}} + Diagram showing the Scheduler control plane service and the jobs API ## How it works @@ -34,19 +39,19 @@ You can use jobs to: Job scheduling can prove helpful in the following scenarios: -- **Automated Database Backups**: +- **Automated Database Backups**: Ensure a database is backed up daily to prevent data loss. Schedule a backup script to run every night at 2 AM, which will create a backup of the database and store it in a secure location. -- **Regular Data Processing and ETL (Extract, Transform, Load)**: +- **Regular Data Processing and ETL (Extract, Transform, Load)**: Process and transform raw data from various sources and load it into a data warehouse. Schedule ETL jobs to run at specific times (for example: hourly, daily) to fetch new data, process it, and update the data warehouse with the latest information. -- **Email Notifications and Reports**: +- **Email Notifications and Reports**: Receive daily sales reports and weekly performance summaries via email. Schedule a job that generates the required reports and sends them via email at 6 a.m. every day for daily reports and 8 a.m. every Monday for weekly summaries. -- **Maintenance Tasks and System Updates**: +- **Maintenance Tasks and System Updates**: Perform regular maintenance tasks such as clearing temporary files, updating software, and checking system health. Schedule various maintenance scripts to run at off-peak hours, such as weekends or late nights, to minimize disruption to users. -- **Batch Processing for Financial Transactions**: +- **Batch Processing for Financial Transactions**: Processes a large number of transactions that need to be batched and settled at the end of each business day. Schedule batch processing jobs to run at 5 PM every business day, aggregating the day’s transactions and performing necessary settlements and reconciliations. Dapr's jobs API ensures the tasks represented in these scenarios are performed consistently and reliably without manual intervention, improving efficiency and reducing the risk of errors. diff --git a/daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md b/daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md new file mode 100644 index 000000000..130122192 --- /dev/null +++ b/daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md @@ -0,0 +1,55 @@ +--- +type: docs +title: "How-to: Persist Scheduler Jobs" +linkTitle: "How-to: Persist Scheduler Jobs" +weight: 50000 +description: "Configure Scheduler to persist its database to make it resilient to restarts" +--- + +The [Scheduler]({{< ref scheduler.md >}}) service is responsible for writing jobs to its embedded database and scheduling them for execution. +By default, the Scheduler service database writes this data to an in-memory ephemeral tempfs volume, meaning that **this data is not persisted across restarts**. Job data will be lost during these events. + +To make the Scheduler data resilient to restarts, a persistent volume must be mounted to the Scheduler `StatefulSet`. +This persistent volume is backed by a real disk that is provided by the hosted Cloud Provider or Kubernetes infrastructure platform. +Disk size is determined by how many jobs are expected to be persisted at once; however, 64Gb should be more than sufficient for most use cases. +Some Kubernetes providers recommend using a [CSI driver](https://kubernetes.io/docs/concepts/storage/volumes/#csi) to provision the underlying disks. +Below are a list of useful links to the relevant documentation for creating a persistent disk for the major cloud providers: +- [Google Cloud Persistent Disk](https://cloud.google.com/compute/docs/disks) +- [Amazon EBS Volumes](https://aws.amazon.com/blogs/storage/persistent-storage-for-kubernetes/) +- [Azure AKS Storage Options](https://learn.microsoft.com/azure/aks/concepts-storage) +- [Digital Ocean Block Storage](https://www.digitalocean.com/docs/kubernetes/how-to/add-volumes/) +- [VMWare vSphere Storage](https://docs.vmware.com/VMware-vSphere/7.0/vmware-vsphere-with-tanzu/GUID-A19F6480-40DC-4343-A5A9-A5D3BFC0742E.html) +- [OpenShift Persistent Storage](https://docs.openshift.com/container-platform/4.6/storage/persistent_storage/persistent-storage-aws-efs.html) +- [Alibaba Cloud Disk Storage](https://www.alibabacloud.com/help/ack/ack-managed-and-ack-dedicated/user-guide/create-a-pvc) + + +Once the persistent volume class is available, you can install Dapr using the following command, with Scheduler configured to use the persistent volume class (replace `my-storage-class` with the name of the storage class): + +{{% alert title="Note" color="primary" %}} +If Dapr is already installed, the control plane needs to be completely [uninstalled]({{< ref dapr-uninstall.md >}}) in order for the Scheduler `StatefulSet` to be recreated with the new persistent volume. +{{% /alert %}} + +{{< tabs "Dapr CLI" "Helm" >}} + +{{% codetab %}} + +```bash +dapr init -k --set dapr_scheduler.cluster.storageClassName=my-storage-class +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```bash +helm upgrade --install dapr dapr/dapr \ +--version={{% dapr-latest-version short="true" %}} \ +--namespace dapr-system \ +--create-namespace \ +--set dapr_scheduler.cluster.storageClassName=my-storage-class \ +--wait +``` + +{{% /codetab %}} +{{< /tabs >}} diff --git a/daprdocs/content/en/operations/hosting/self-hosted/self-hosted-persisting-scheduler.md b/daprdocs/content/en/operations/hosting/self-hosted/self-hosted-persisting-scheduler.md new file mode 100644 index 000000000..5bcc8745f --- /dev/null +++ b/daprdocs/content/en/operations/hosting/self-hosted/self-hosted-persisting-scheduler.md @@ -0,0 +1,27 @@ +--- +type: docs +title: "How-to: Persist Scheduler Jobs" +linkTitle: "How-to: Persist Scheduler Jobs" +weight: 50000 +description: "Configure Scheduler to persist its database to make it resilient to restarts" +--- + +The [Scheduler]({{< ref scheduler.md >}}) service is responsible for writing jobs to its embedded database and scheduling them for execution. +By default, the Scheduler service database writes this data to the local volume `dapr_scheduler`, meaning that **this data is persisted across restarts**. + +The host file location for this local volume is typically located at either `/var/lib/docker/volumes/dapr_scheduler/_data` or `~/.local/share/containers/storage/volumes/dapr_scheduler/_data`, depending on your container runtime. +Note that if you are using Docker Desktop, this volume is located in the Docker Desktop VM's filesystem, which can be accessed using: + +```bash +docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh +``` + +The Scheduler persistent volume can be modified with a custom volume that is pre-existing, or is created by Dapr. + +{{% alert title="Note" color="primary" %}} +By default `dapr init` creates a local persistent volume on your drive called `dapr_scheduler`. If Dapr is already installed, the control plane needs to be completely [uninstalled]({{< ref dapr-uninstall.md >}}) in order for the Scheduler container to be recreated with the new persistent volume. +{{% /alert %}} + +```bash +dapr init --scheduler-volume my-scheduler-volume +``` diff --git a/daprdocs/content/en/reference/api/jobs_api.md b/daprdocs/content/en/reference/api/jobs_api.md index de9c33b0d..37332b848 100644 --- a/daprdocs/content/en/reference/api/jobs_api.md +++ b/daprdocs/content/en/reference/api/jobs_api.md @@ -10,8 +10,16 @@ weight: 1300 The jobs API is currently in alpha. {{% /alert %}} +{{% alert title="Warning" color="warning" %}} +By default, job data is not resilient to [Scheduler]({{< ref scheduler.md >}}) service restarts. +A persistent volume must be provided to Scheduler to ensure job data is not lost in either [Kubernetes]({{< ref kubernetes-persisting-scheduler.md >}}) or [Self-Hosted]({{< ref self-hosted-persisting-scheduler.md >}}) mode. +{{% /alert %}} + With the jobs API, you can schedule jobs and tasks in the future. +> The HTTP APIs are intended for development and testing only. For production scenarios, the use of the SDKs is strongly +> recommended as they implement the gRPC APIs providing higher performance and capability than the HTTP APIs. + ## Schedule a job Schedule a job with a name. @@ -22,22 +30,50 @@ POST http://localhost:3500/v1.0-alpha1/jobs/ ### URL parameters +{{% alert title="Note" color="primary" %}} +At least one of `schedule` or `dueTime` must be provided, but they can also be provided together. +{{% /alert %}} + Parameter | Description --------- | ----------- `name` | Name of the job you're scheduling -`data` | A string value and can be any related content. Content is returned when the reminder expires. For example, this may be useful for returning a URL or anything related to the content. -`dueTime` | Specifies the time after which this job is invoked. Its format should be [time.ParseDuration](https://pkg.go.dev/time#ParseDuration) +`data` | A protobuf message `@type`/`value` pair. `@type` must be of a [well-known type](https://protobuf.dev/reference/protobuf/google.protobuf). `value` is the serialized data. +`schedule` | An optional schedule at which the job is to be run. Details of the format are below. +`dueTime` | An optional time at which the job should be active, or the "one shot" time, if other scheduling type fields are not provided. Accepts a "point in time" string in the format of RFC3339, Go duration string (calculated from creation time), or non-repeating ISO8601. +`repeats` | An optional number of times in which the job should be triggered. If not set, the job runs indefinitely or until expiration. +`ttl` | An optional time to live or expiration of the job. Accepts a "point in time" string in the format of RFC3339, Go duration string (calculated from job creation time), or non-repeating ISO8601. + +#### schedule +`schedule` accepts both systemd timer-style cron expressions, as well as human readable '@' prefixed period strings, as defined below. + +Systemd timer style cron accepts 6 fields: +seconds | minutes | hours | day of month | month | day of week +0-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-7/sun-sat + +"0 30 * * * *" - every hour on the half hour +"0 15 3 * * *" - every day at 03:15 + +Period string expressions: +Entry | Description | Equivalent To +----- | ----------- | ------------- +@every | Run every (e.g. '@every 1h30m') | N/A +@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * +@monthly | Run once a month, midnight, first of month | 0 0 0 1 * * +@weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 +@daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * +@hourly | Run once an hour, beginning of hour | 0 0 * * * * + ### Request body ```json { "job": { - "data": { - "@type": "type.googleapis.com/google.type.Expr", - "expression": "" - }, - "dueTime": "30s" + "data": { + "@type": "type.googleapis.com/google.protobuf.StringValue", + "value": "\"someData\"" + }, + "dueTime": "30s" } } ``` @@ -46,24 +82,26 @@ Parameter | Description Code | Description ---- | ----------- -`202` | Accepted +`204` | Accepted `400` | Request was malformed `500` | Request formatted correctly, error in dapr code or Scheduler control plane service ### Response content -The following example curl command creates a job, naming the job `jobforjabba` and specifying the `dueTime` and the `data`. +The following example curl command creates a job, naming the job `jobforjabba` and specifying the `schedule`, `repeats` and the `data`. ```bash $ curl -X POST \ http://localhost:3500/v1.0-alpha1/jobs/jobforjabba \ - -H "Content-Type: application/json" + -H "Content-Type: application/json" -d '{ "job": { "data": { - "HanSolo": "Running spice" + "@type": "type.googleapis.com/google.protobuf.StringValue", + "value": "Running spice" }, - "dueTime": "30s" + "schedule": "@every 1m", + "repeats": 5 } }' ``` @@ -87,33 +125,35 @@ Parameter | Description Code | Description ---- | ----------- -`202` | Accepted +`200` | Accepted `400` | Request was malformed -`500` | Request formatted correctly, error in dapr code or Scheduler control plane service +`500` | Request formatted correctly, Job doesn't exist or error in dapr code or Scheduler control plane service ### Response content After running the following example curl command, the returned response is JSON containing the `name` of the job, the `dueTime`, and the `data`. ```bash -$ curl -X GET http://localhost:3500/v1.0-alpha1/jobs/jobforjabba -H "Content-Type: application/json" +$ curl -X GET http://localhost:3500/v1.0-alpha1/jobs/jobforjabba -H "Content-Type: application/json" ``` ```json { - "name":"test1", - "dueTime":"30s", + "name": "jobforjabba", + "schedule": "@every 1m", + "repeats": 5, "data": { - "HanSolo": "Running spice" - } -} + "@type": "type.googleapis.com/google.protobuf.StringValue", + "value": "Running spice" + } +} ``` ## Delete a job Delete a named job. ``` -DELETE http://localhost:3500/v1.0-alpha1/jobs/ +DELETE http://localhost:3500/v1.0-alpha1/jobs/ ``` ### URL parameters @@ -126,7 +166,7 @@ Parameter | Description Code | Description ---- | ----------- -`202` | Accepted +`204` | Accepted `400` | Request was malformed `500` | Request formatted correctly, error in dapr code or Scheduler control plane service @@ -135,7 +175,7 @@ Code | Description In the following example curl command, the job named `test1` with app-id `sub` will be deleted ```bash -$ curl -X DELETE http://localhost:3500/v1.0-alpha1/jobs/jobforjabba -H "Content-Type: application/json" +$ curl -X DELETE http://localhost:3500/v1.0-alpha1/jobs/jobforjabba -H "Content-Type: application/json" ``` diff --git a/daprdocs/content/en/reference/cli/dapr-init.md b/daprdocs/content/en/reference/cli/dapr-init.md index b9295a01d..3df59f323 100644 --- a/daprdocs/content/en/reference/cli/dapr-init.md +++ b/daprdocs/content/en/reference/cli/dapr-init.md @@ -45,6 +45,7 @@ dapr init [flags] | N/A | DAPR_HELM_REPO_PASSWORD | A password for a private Helm chart |The password required to access the private Dapr Helm chart. If it can be accessed publicly, this env variable does not need to be set| | | `--container-runtime` | | `docker` | Used to pass in a different container runtime other than Docker. Supported container runtimes are: `docker`, `podman` | | `--dev` | | | Creates Redis and Zipkin deployments when run in Kubernetes. | +| `--scheduler-volume` | | | Self-hosted only. Optionally, you can specify a volume for the scheduler service data directory. By default, without this flag, scheduler data is not persisted and not resilient to restarts. | ### Examples @@ -55,7 +56,9 @@ dapr init [flags] **Install** -Install Dapr by pulling container images for Placement, Scheduler, Redis, and Zipkin. By default, these images are pulled from Docker Hub. +Install Dapr by pulling container images for Placement, Scheduler, Redis, and Zipkin. By default, these images are pulled from Docker Hub. + +> By default, a `dapr_scheduler` local volume is created for Scheduler service to be used as the database directory. The host file location for this volume is likely located at `/var/lib/docker/volumes/dapr_scheduler/_data` or `~/.local/share/containers/storage/volumes/dapr_scheduler/_data`, depending on your container runtime. ```bash dapr init From a00e043ca1a4a4c4b7bf12233b6322cf31c06ad2 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 29 Jul 2024 11:40:12 -0400 Subject: [PATCH 59/92] update per fernando and mark review Signed-off-by: Hannah Hunter --- .../quickstarts/jobs-quickstart.md | 318 +++++++++++++++++- 1 file changed, 301 insertions(+), 17 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index d9c1cb3c4..9be5deae8 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -12,15 +12,11 @@ The jobs building block is currently in **alpha**. Let's take a look at the [Dapr jobs building block]({{< ref jobs-overview.md >}}), which schedules and runs jobs at a specific time or interval. In this Quickstart, you'll schedule, get, and delete a job using Dapr's Job API. -Need diagram - - -You can try out this pub/sub quickstart by either: +You can try out this jobs quickstart by either: - [Running all applications in this sample simultaneously with the Multi-App Run template file]({{< ref "#run-using-multi-app-run" >}}), or - [Running one application at a time]({{< ref "#run-one-job-application-at-a-time" >}}) - ## Run using Multi-App Run Select your preferred language-specific Dapr SDK before proceeding with the Quickstart. Currently, you can experiment with the jobs API with the Go SDK. @@ -144,7 +140,7 @@ apps: #### `job-service` app -The `job-service` application creates handlers for the job service. +The `job-service` application creates service invocation handlers to manage the lifecycle of the job (`scheduleJob`, `getJob`, and `deleteJob`). ```go if err := server.AddServiceInvocationHandler("scheduleJob", scheduleJob); err != nil { @@ -160,7 +156,7 @@ if err := server.AddServiceInvocationHandler("deleteJob", deleteJob); err != nil } ``` -Next, the jobs event handler is registered: +Next, job event handlers are registered for all droids: ```go for _, jobName := range jobNames { @@ -176,7 +172,7 @@ if err = server.Start(); err != nil { } ``` -The `job-service` then codes the functions that handle scheduling, getting, and deleting jobs, and handles job events. +The `job-service` then codes the functions that handle scheduling, getting, deleting, and handling job events. ```go // Handler that schedules a DroidJob @@ -318,8 +314,6 @@ err = schedule(droidJobs[0]) if err != nil { log.Fatalln("Error scheduling job: ", err) } - -time.Sleep(3 * time.Second) ``` Then, the C3PO job is scheduled, and returns job data: @@ -330,7 +324,6 @@ err = schedule(droidJobs[1]) if err != nil { log.Fatalln("Error scheduling job: ", err) } -time.Sleep(5 * time.Second) // Get C-3PO job resp, err := get(droidJobs[1]) @@ -349,8 +342,6 @@ if err != nil { log.Fatalln("Error scheduling job: ", err) } -time.Sleep(5 * time.Second) - // Get BB-8 job resp, err = get(droidJobs[2]) if err != nil { @@ -358,8 +349,6 @@ if err != nil { } fmt.Println("Get job response: ", resp) -time.Sleep(5 * time.Second) - // Delete BB-8 job err = delete(droidJobs[2]) if err != nil { @@ -473,7 +462,7 @@ cd jobs/go/sdk In the terminal, run the `job-service` app: ```bash -dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 --dapr-grpc-port 6281 --app-protocol grpc -- go run . +dapr run --app-id job-service --app-port 6200 --dapr-grpc-port 6281 --app-protocol grpc -- go run . ``` **Expected output** @@ -515,6 +504,301 @@ Return to the `job-service` app terminal window. The output should be: == APP == Executing maintenance job: Memory Wipe ``` +### What happened? + +When you ran the `dapr run` in this Quickstart for both the `job-scheduler` and the `job-service`, you can see the following jobs being registered, scheduled, retrieved, and deleted. + +- The `R2-D2` job is registered. +- The `C-3PO` job is registered. +- The `BB-8` job is registered. +- The `R2-D2` job is being scheduled. +- The `C-3PO` job is being scheduled. +- The `C-3PO` job is being retrieved. +- The `BB-8` job is being scheduled. +- The `BB-8` job is being retrieved. +- The `BB-8` job is being deleted. +- The `R2-D2` job is being executed after 5 seconds. +- The `R2-D2` job is being executed after 10 seconds. + +#### `job-service` app + +The `job-service` application creates service invocation handlers to manage the lifecycle of the job (`scheduleJob`, `getJob`, and `deleteJob`). + +```go +if err := server.AddServiceInvocationHandler("scheduleJob", scheduleJob); err != nil { + log.Fatalf("error adding invocation handler: %v", err) +} + +if err := server.AddServiceInvocationHandler("getJob", getJob); err != nil { + log.Fatalf("error adding invocation handler: %v", err) +} + +if err := server.AddServiceInvocationHandler("deleteJob", deleteJob); err != nil { + log.Fatalf("error adding invocation handler: %v", err) +} +``` + +Next, job event handlers are registered for all droids: + +```go +for _, jobName := range jobNames { + if err := server.AddJobEventHandler(jobName, handleJob); err != nil { + log.Fatalf("failed to register job event handler: %v", err) + } + fmt.Println("Registered job handler for: ", jobName) +} + +fmt.Println("Starting server on port: " + appPort) +if err = server.Start(); err != nil { + log.Fatalf("failed to start server: %v", err) +} +``` + +The `job-service` then codes the functions that handle scheduling, getting, deleting, and handling job events. + +```go +// Handler that schedules a DroidJob +func scheduleJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + + if in == nil { + err = errors.New("no invocation parameter") + return + } + + droidJob := DroidJob{} + err = json.Unmarshal(in.Data, &droidJob) + if err != nil { + fmt.Println("failed to unmarshal job: ", err) + return nil, err + } + + jobData := JobData{ + Droid: droidJob.Name, + Task: droidJob.Job, + } + + content, err := json.Marshal(jobData) + if err != nil { + fmt.Printf("Error marshalling job content") + return nil, err + } + + // schedule job + job := daprc.Job{ + Name: droidJob.Name, + DueTime: droidJob.DueTime, + Data: &anypb.Any{ + Value: content, + }, + } + + err = app.daprClient.ScheduleJobAlpha1(ctx, &job) + if err != nil { + fmt.Println("failed to schedule job. err: ", err) + return nil, err + } + + fmt.Println("Job scheduled: ", droidJob.Name) + + out = &common.Content{ + Data: in.Data, + ContentType: in.ContentType, + DataTypeURL: in.DataTypeURL, + } + + return out, err + +} + +// Handler that gets a job by name +func getJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + + if in == nil { + err = errors.New("no invocation parameter") + return nil, err + } + + job, err := app.daprClient.GetJobAlpha1(ctx, string(in.Data)) + if err != nil { + fmt.Println("failed to get job. err: ", err) + } + + out = &common.Content{ + Data: job.Data.Value, + ContentType: in.ContentType, + DataTypeURL: in.DataTypeURL, + } + + return out, err +} + +// Handler that deletes a job by name +func deleteJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { + if in == nil { + err = errors.New("no invocation parameter") + return nil, err + } + + err = app.daprClient.DeleteJobAlpha1(ctx, string(in.Data)) + if err != nil { + fmt.Println("failed to delete job. err: ", err) + } + + out = &common.Content{ + Data: in.Data, + ContentType: in.ContentType, + DataTypeURL: in.DataTypeURL, + } + + return out, err +} + +// Handler that handles job events +func handleJob(ctx context.Context, job *common.JobEvent) error { + var jobData common.Job + if err := json.Unmarshal(job.Data, &jobData); err != nil { + return fmt.Errorf("failed to unmarshal job: %v", err) + } + decodedPayload, err := base64.StdEncoding.DecodeString(jobData.Value) + if err != nil { + return fmt.Errorf("failed to decode job payload: %v", err) + } + var jobPayload JobData + if err := json.Unmarshal(decodedPayload, &jobPayload); err != nil { + return fmt.Errorf("failed to unmarshal payload: %v", err) + } + + fmt.Println("Starting droid:", jobPayload.Droid) + fmt.Println("Executing maintenance job:", jobPayload.Task) + + return nil +} +``` + +#### `job-scheduler` app + +In the `job-scheduler` application, the R2D2, C3PO, and BB8 jobs are first defined as `[]DroidJob`: + +```go +droidJobs := []DroidJob{ + {Name: "R2-D2", Job: "Oil Change", DueTime: "5s"}, + {Name: "C-3PO", Job: "Memory Wipe", DueTime: "15s"}, + {Name: "BB-8", Job: "Internal Gyroscope Check", DueTime: "30s"}, +} +``` + + +The jobs are then scheduled, retrieved, and deleted using the jobs API. As you can see from the terminal output, first the R2D2 job is scheduled: + +```go +// Schedule R2D2 job +err = schedule(droidJobs[0]) +if err != nil { + log.Fatalln("Error scheduling job: ", err) +} +``` + +Then, the C3PO job is scheduled, and returns job data: + +```go +// Schedule C-3PO job +err = schedule(droidJobs[1]) +if err != nil { + log.Fatalln("Error scheduling job: ", err) +} + +// Get C-3PO job +resp, err := get(droidJobs[1]) +if err != nil { + log.Fatalln("Error retrieving job: ", err) +} +fmt.Println("Get job response: ", resp) +``` + +The BB8 job is then scheduled, retrieved, and deleted: + +```go +// Schedule BB-8 job +err = schedule(droidJobs[2]) +if err != nil { + log.Fatalln("Error scheduling job: ", err) +} + +// Get BB-8 job +resp, err = get(droidJobs[2]) +if err != nil { + log.Fatalln("Error retrieving job: ", err) +} +fmt.Println("Get job response: ", resp) + +// Delete BB-8 job +err = delete(droidJobs[2]) +if err != nil { + log.Fatalln("Error deleting job: ", err) +} +fmt.Println("Job deleted: ", droidJobs[2].Name) +``` + +The `job-scheduler.go` also defines the `schedule`, `get`, and `delete` functions, calling from `job-service.go`. + +```go +// Schedules a job by invoking grpc service from job-service passing a DroidJob as an argument +func schedule(droidJob DroidJob) error { + jobData, err := json.Marshal(droidJob) + if err != nil { + fmt.Println("Error marshalling job content") + return err + } + + content := &daprc.DataContent{ + ContentType: "application/json", + Data: []byte(jobData), + } + + // Schedule Job + _, err = app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "scheduleJob", "POST", content) + if err != nil { + fmt.Println("Error invoking method: ", err) + return err + } + + return nil +} + +// Gets a job by invoking grpc service from job-service passing a job name as an argument +func get(droidJob DroidJob) (string, error) { + content := &daprc.DataContent{ + ContentType: "text/plain", + Data: []byte(droidJob.Name), + } + + //get job + resp, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "getJob", "GET", content) + if err != nil { + fmt.Println("Error invoking method: ", err) + return "", err + } + + return string(resp), nil +} + +// Deletes a job by invoking grpc service from job-service passing a job name as an argument +func delete(droidJob DroidJob) error { + content := &daprc.DataContent{ + ContentType: "text/plain", + Data: []byte(droidJob.Name), + } + + _, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "deleteJob", "DELETE", content) + if err != nil { + fmt.Println("Error invoking method: ", err) + return err + } + + return nil +} +``` + {{% /codetab %}} {{< /tabs >}} @@ -535,7 +819,7 @@ Join the discussion in our [discord channel](https://discord.com/channels/778680 ## Next steps - HTTP samples of this quickstart: - - [Go](todo) + - [Go](https://github.com/dapr/quickstarts/tree/master/jobs/go/http) - Learn more about [the jobs building block]({{< ref jobs-overview.md >}}) - Learn more about [the scheduler control plane]({{< ref scheduler.md >}}) From c7f303fa4725a153fff7c7c43c42f65b0400cdca Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 29 Jul 2024 12:21:25 -0400 Subject: [PATCH 60/92] pull in cassie change and edit Signed-off-by: Hannah Hunter --- ...owto-schedule-and-handle-triggered-jobs.md | 56 +++++++------------ .../building-blocks/jobs/jobs-overview.md | 4 +- 2 files changed, 22 insertions(+), 38 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md index 9d5740d27..400068090 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md @@ -26,11 +26,12 @@ In your code, set up and schedule jobs within your application. -The following Go SDK code sample schedules the job named `prod-db-backup`. Job data is housed in a backup database (`"my-prod-db"`) and is scheduled with `ScheduleJobAlpha1`, -providing the job `data` which is the backup `task` name and `metadata` including the location and database name. The job is scheduled with a `schedule` set and the amount of `repeats` -desired, meaning there is a max amount of times the job should be triggered and sent back to the app. At trigger time, so `@every 1s` according to the `schedule`, this job will be -triggered and sent back to the application up to the max repeats set which is `10`. At the trigger time, the `prodDBBackupHandler` function is called executing the -desired business logic for this job at trigger time. For example: +The following Go SDK code sample schedules the job named `prod-db-backup`. Job data is housed in a backup database (`"my-prod-db"`) and is scheduled with `ScheduleJobAlpha1`. This provides the `jobData`, which includes: +- The backup `Task` name +- The backup task's `Metadata`, including: + - The database name (`DBName`) + - The database location (`BackupLocation`) + ```go package main @@ -71,7 +72,15 @@ func main() { }, ) // ... - +} +``` + +The job is scheduled with a `Schedule` set and the amount of `Repeats` desired. These settings determine a max amount of times the job should be triggered and sent back to the app. + +In this example, at trigger time, which is `@every 1s` according to the `Schedule`, this job is triggered and sent back to the application up to the max `Repeats` (`10`). + +```go + // ... // Set up the job job := daprc.Job{ Name: "prod-db-backup", @@ -81,37 +90,14 @@ func main() { Value: jobData, }, } +``` - // Create the client - client, err := daprc.NewClient() - // ... +At the trigger time, the `prodDBBackupHandler` function is called, executing the desired business logic for this job at trigger time. For example: - defer client.Close() +```go +// ... - // Schedule job - err = client.ScheduleJobAlpha1(ctx, &job) - // ... - fmt.Println("schedule job - success") - - // Get job - resp, err := client.GetJobAlpha1(ctx, "prod-db-backup") - // ... - fmt.Printf("get job - resp: %v\n", resp) // parse - - // Let test run and then cleanup the job - time.Sleep(20 * time.Second) - // Delete job - err = client.DeleteJobAlpha1(ctx, "prod-db-backup") - // ... - - if err = server.Stop(); err != nil { - log.Fatalf("failed to stop server: %v\n", err) - } -} - -var jobCount = 0 - -// at job trigger time this function is called +// At job trigger time this function is called func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error { var jobData common.Job if err := json.Unmarshal(job.Data, &jobData); err != nil { @@ -128,12 +114,10 @@ func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error { jobCount++ return nil } - ``` {{% /codetab %}} - {{< /tabs >}} ## Run the Dapr sidecar diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md index ffea762f2..1371a657a 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md @@ -65,10 +65,10 @@ Actors have actor reminders, but present some limitations involving scalability ## Try out the jobs API -You can try out the jobs API in your application. After [Dapr is installed]({{< ref install-dapr-cli.md >}}), you can begin using the jobs API, starting with [the How-to: Schedule jobs guide]({{< ref howto-schedule-jobs.md >}}). +You can try out the jobs API in your application. After [Dapr is installed]({{< ref install-dapr-cli.md >}}), you can begin using the jobs API, starting with [the How-to: Schedule jobs guide]({{< ref howto-schedule-and-handle-triggered-jobs.md >}}). ## Next steps -- [Learn how to use the jobs API]({{< ref howto-schedule-jobs.md >}}) +- [Learn how to use the jobs API]({{< ref howto-schedule-and-handle-triggered-jobs.md >}}) - [Learn more about the Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) - [Jobs API reference]({{< ref jobs_api.md >}}) From 33f80b61d59a6927d49d9e5d4ca999551ee36389 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 29 Jul 2024 12:26:35 -0400 Subject: [PATCH 61/92] add code tabs for dapr run Signed-off-by: Hannah Hunter --- .../jobs/howto-schedule-and-handle-triggered-jobs.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md index 400068090..27dc31fc2 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/howto-schedule-and-handle-triggered-jobs.md @@ -124,6 +124,10 @@ func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error { Once you've set up the Jobs API in your application, in a terminal window run the Dapr sidecar with the following command. +{{< tabs "Go" >}} + +{{% codetab %}} + ```bash dapr run --app-id=distributed-scheduler \ --metrics-port=9091 \ @@ -134,6 +138,11 @@ dapr run --app-id=distributed-scheduler \ go run ./main.go ``` +{{% /codetab %}} + +{{< /tabs >}} + + ## Next steps - [Learn more about the Scheduler control plane service]({{< ref "concepts/dapr-services/scheduler.md" >}}) From 434bbff274663af007ba78a5f63a488034ff7f08 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 29 Jul 2024 12:52:06 -0400 Subject: [PATCH 62/92] confirm code is up to date and link to what happened sections Signed-off-by: Hannah Hunter --- .../quickstarts/jobs-quickstart.md | 295 +----------------- 1 file changed, 1 insertion(+), 294 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index 9be5deae8..a14f3c310 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -504,300 +504,7 @@ Return to the `job-service` app terminal window. The output should be: == APP == Executing maintenance job: Memory Wipe ``` -### What happened? - -When you ran the `dapr run` in this Quickstart for both the `job-scheduler` and the `job-service`, you can see the following jobs being registered, scheduled, retrieved, and deleted. - -- The `R2-D2` job is registered. -- The `C-3PO` job is registered. -- The `BB-8` job is registered. -- The `R2-D2` job is being scheduled. -- The `C-3PO` job is being scheduled. -- The `C-3PO` job is being retrieved. -- The `BB-8` job is being scheduled. -- The `BB-8` job is being retrieved. -- The `BB-8` job is being deleted. -- The `R2-D2` job is being executed after 5 seconds. -- The `R2-D2` job is being executed after 10 seconds. - -#### `job-service` app - -The `job-service` application creates service invocation handlers to manage the lifecycle of the job (`scheduleJob`, `getJob`, and `deleteJob`). - -```go -if err := server.AddServiceInvocationHandler("scheduleJob", scheduleJob); err != nil { - log.Fatalf("error adding invocation handler: %v", err) -} - -if err := server.AddServiceInvocationHandler("getJob", getJob); err != nil { - log.Fatalf("error adding invocation handler: %v", err) -} - -if err := server.AddServiceInvocationHandler("deleteJob", deleteJob); err != nil { - log.Fatalf("error adding invocation handler: %v", err) -} -``` - -Next, job event handlers are registered for all droids: - -```go -for _, jobName := range jobNames { - if err := server.AddJobEventHandler(jobName, handleJob); err != nil { - log.Fatalf("failed to register job event handler: %v", err) - } - fmt.Println("Registered job handler for: ", jobName) -} - -fmt.Println("Starting server on port: " + appPort) -if err = server.Start(); err != nil { - log.Fatalf("failed to start server: %v", err) -} -``` - -The `job-service` then codes the functions that handle scheduling, getting, deleting, and handling job events. - -```go -// Handler that schedules a DroidJob -func scheduleJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - - if in == nil { - err = errors.New("no invocation parameter") - return - } - - droidJob := DroidJob{} - err = json.Unmarshal(in.Data, &droidJob) - if err != nil { - fmt.Println("failed to unmarshal job: ", err) - return nil, err - } - - jobData := JobData{ - Droid: droidJob.Name, - Task: droidJob.Job, - } - - content, err := json.Marshal(jobData) - if err != nil { - fmt.Printf("Error marshalling job content") - return nil, err - } - - // schedule job - job := daprc.Job{ - Name: droidJob.Name, - DueTime: droidJob.DueTime, - Data: &anypb.Any{ - Value: content, - }, - } - - err = app.daprClient.ScheduleJobAlpha1(ctx, &job) - if err != nil { - fmt.Println("failed to schedule job. err: ", err) - return nil, err - } - - fmt.Println("Job scheduled: ", droidJob.Name) - - out = &common.Content{ - Data: in.Data, - ContentType: in.ContentType, - DataTypeURL: in.DataTypeURL, - } - - return out, err - -} - -// Handler that gets a job by name -func getJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - - if in == nil { - err = errors.New("no invocation parameter") - return nil, err - } - - job, err := app.daprClient.GetJobAlpha1(ctx, string(in.Data)) - if err != nil { - fmt.Println("failed to get job. err: ", err) - } - - out = &common.Content{ - Data: job.Data.Value, - ContentType: in.ContentType, - DataTypeURL: in.DataTypeURL, - } - - return out, err -} - -// Handler that deletes a job by name -func deleteJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { - if in == nil { - err = errors.New("no invocation parameter") - return nil, err - } - - err = app.daprClient.DeleteJobAlpha1(ctx, string(in.Data)) - if err != nil { - fmt.Println("failed to delete job. err: ", err) - } - - out = &common.Content{ - Data: in.Data, - ContentType: in.ContentType, - DataTypeURL: in.DataTypeURL, - } - - return out, err -} - -// Handler that handles job events -func handleJob(ctx context.Context, job *common.JobEvent) error { - var jobData common.Job - if err := json.Unmarshal(job.Data, &jobData); err != nil { - return fmt.Errorf("failed to unmarshal job: %v", err) - } - decodedPayload, err := base64.StdEncoding.DecodeString(jobData.Value) - if err != nil { - return fmt.Errorf("failed to decode job payload: %v", err) - } - var jobPayload JobData - if err := json.Unmarshal(decodedPayload, &jobPayload); err != nil { - return fmt.Errorf("failed to unmarshal payload: %v", err) - } - - fmt.Println("Starting droid:", jobPayload.Droid) - fmt.Println("Executing maintenance job:", jobPayload.Task) - - return nil -} -``` - -#### `job-scheduler` app - -In the `job-scheduler` application, the R2D2, C3PO, and BB8 jobs are first defined as `[]DroidJob`: - -```go -droidJobs := []DroidJob{ - {Name: "R2-D2", Job: "Oil Change", DueTime: "5s"}, - {Name: "C-3PO", Job: "Memory Wipe", DueTime: "15s"}, - {Name: "BB-8", Job: "Internal Gyroscope Check", DueTime: "30s"}, -} -``` - - -The jobs are then scheduled, retrieved, and deleted using the jobs API. As you can see from the terminal output, first the R2D2 job is scheduled: - -```go -// Schedule R2D2 job -err = schedule(droidJobs[0]) -if err != nil { - log.Fatalln("Error scheduling job: ", err) -} -``` - -Then, the C3PO job is scheduled, and returns job data: - -```go -// Schedule C-3PO job -err = schedule(droidJobs[1]) -if err != nil { - log.Fatalln("Error scheduling job: ", err) -} - -// Get C-3PO job -resp, err := get(droidJobs[1]) -if err != nil { - log.Fatalln("Error retrieving job: ", err) -} -fmt.Println("Get job response: ", resp) -``` - -The BB8 job is then scheduled, retrieved, and deleted: - -```go -// Schedule BB-8 job -err = schedule(droidJobs[2]) -if err != nil { - log.Fatalln("Error scheduling job: ", err) -} - -// Get BB-8 job -resp, err = get(droidJobs[2]) -if err != nil { - log.Fatalln("Error retrieving job: ", err) -} -fmt.Println("Get job response: ", resp) - -// Delete BB-8 job -err = delete(droidJobs[2]) -if err != nil { - log.Fatalln("Error deleting job: ", err) -} -fmt.Println("Job deleted: ", droidJobs[2].Name) -``` - -The `job-scheduler.go` also defines the `schedule`, `get`, and `delete` functions, calling from `job-service.go`. - -```go -// Schedules a job by invoking grpc service from job-service passing a DroidJob as an argument -func schedule(droidJob DroidJob) error { - jobData, err := json.Marshal(droidJob) - if err != nil { - fmt.Println("Error marshalling job content") - return err - } - - content := &daprc.DataContent{ - ContentType: "application/json", - Data: []byte(jobData), - } - - // Schedule Job - _, err = app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "scheduleJob", "POST", content) - if err != nil { - fmt.Println("Error invoking method: ", err) - return err - } - - return nil -} - -// Gets a job by invoking grpc service from job-service passing a job name as an argument -func get(droidJob DroidJob) (string, error) { - content := &daprc.DataContent{ - ContentType: "text/plain", - Data: []byte(droidJob.Name), - } - - //get job - resp, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "getJob", "GET", content) - if err != nil { - fmt.Println("Error invoking method: ", err) - return "", err - } - - return string(resp), nil -} - -// Deletes a job by invoking grpc service from job-service passing a job name as an argument -func delete(droidJob DroidJob) error { - content := &daprc.DataContent{ - ContentType: "text/plain", - Data: []byte(droidJob.Name), - } - - _, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "deleteJob", "DELETE", content) - if err != nil { - fmt.Println("Error invoking method: ", err) - return err - } - - return nil -} -``` +Unpack what happened in the [`job-service`]({{< ref "#job-service-app" >}}) and [`job-scheduler`]({{< ref "#job-scheduler-app" >}}) applications when you ran `dapr run`. {{% /codetab %}} From 7453e286a6d2a16832ef387984c0275ce24cd229 Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:14:13 -0400 Subject: [PATCH 63/92] Update daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md Co-authored-by: Mark Fussell Signed-off-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> --- .../content/en/getting-started/quickstarts/jobs-quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index a14f3c310..963105186 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -172,7 +172,7 @@ if err = server.Start(); err != nil { } ``` -The `job-service` then codes the functions that handle scheduling, getting, deleting, and handling job events. +The `job-service` then call functions that handle scheduling, getting, deleting, and handling job events. ```go // Handler that schedules a DroidJob From 4252c619bb2f8e6288dd17535709eb8dba5ead55 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 29 Jul 2024 14:42:04 -0400 Subject: [PATCH 64/92] update sdk and dashboard releases Signed-off-by: Hannah Hunter --- .../content/en/operations/support/support-release-policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index 1ecc51f0c..77a852159 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,7 +45,7 @@ The table below shows the versions of Dapr releases that have been tested togeth | Release date | Runtime | CLI | SDKs | Dashboard | Status | Release notes | |--------------------|:--------:|:--------|---------|---------|---------|------------| -| July 24th 2024 | 1.14.0
| 1.14.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | +| July 24th 2024 | 1.14.0
| 1.14.0 | Java 1.12.0
Go 1.11.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.1 | 0.15.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | | May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | | May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | | April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | From 155219498eb02b2436936d92b6e641b1b6ac314a Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 29 Jul 2024 15:50:47 -0400 Subject: [PATCH 65/92] add jobs to qs table Signed-off-by: Hannah Hunter --- daprdocs/content/en/getting-started/quickstarts/_index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daprdocs/content/en/getting-started/quickstarts/_index.md b/daprdocs/content/en/getting-started/quickstarts/_index.md index 6507ee635..dbb02df4d 100644 --- a/daprdocs/content/en/getting-started/quickstarts/_index.md +++ b/daprdocs/content/en/getting-started/quickstarts/_index.md @@ -32,3 +32,5 @@ Hit the ground running with our Dapr quickstarts, complete with code samples aim | [Configuration]({{< ref configuration-quickstart.md >}}) | Get configuration items and subscribe for configuration updates. | | [Resiliency]({{< ref resiliency >}}) | Define and apply fault-tolerance policies to your Dapr API requests. | | [Cryptography]({{< ref cryptography-quickstart.md >}}) | Encrypt and decrypt data using Dapr's cryptographic APIs. | +| [Jobs]({{< ref jobs-quickstart.md >}}) | Schedule, retrieve, and delete jobs using Dapr's jobs APIs. | + From 298a2ce4d88de319bc6fa44c8ecd8bd4cbdb0bf7 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Tue, 30 Jul 2024 13:56:50 -0400 Subject: [PATCH 66/92] link to specific language in quickstarts repo Signed-off-by: Hannah Hunter --- .../quickstarts/actors-quickstart.md | 2 +- .../quickstarts/bindings-quickstart.md | 10 +++++----- .../quickstarts/configuration-quickstart.md | 14 ++++++------- .../quickstarts/cryptography-quickstart.md | 4 ++-- .../quickstarts/jobs-quickstart.md | 2 +- .../quickstarts/pubsub-quickstart.md | 20 +++++++++---------- .../resiliency-serviceinvo-quickstart.md | 10 +++++----- .../resiliency/resiliency-state-quickstart.md | 10 +++++----- .../quickstarts/secrets-quickstart.md | 10 +++++----- .../serviceinvocation-quickstart.md | 10 +++++----- .../quickstarts/statemanagement-quickstart.md | 20 +++++++++---------- .../quickstarts/workflow-quickstart.md | 10 +++++----- 12 files changed, 61 insertions(+), 61 deletions(-) diff --git a/daprdocs/content/en/getting-started/quickstarts/actors-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/actors-quickstart.md index ab1d89d00..a412cc014 100644 --- a/daprdocs/content/en/getting-started/quickstarts/actors-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/actors-quickstart.md @@ -37,7 +37,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/actors). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/actors/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md index 4e40a5159..9c234528a 100644 --- a/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/bindings-quickstart.md @@ -33,7 +33,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -244,7 +244,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -450,7 +450,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -661,7 +661,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -872,7 +872,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/bindings/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md index a9c563ba1..0b37d528a 100644 --- a/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/configuration-quickstart.md @@ -35,7 +35,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -151,14 +151,14 @@ if unsubscribed == True: For this example, you will need: - [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- [Python 3.7+ installed](https://www.python.org/downloads/). - +- [Latest Node.js installed](https://nodejs.org/download/). + - [Docker Desktop](https://www.docker.com/products/docker-desktop) ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -279,7 +279,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -399,7 +399,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -514,7 +514,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/configuration/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/cryptography-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/cryptography-quickstart.md index 8959672d0..76ace1305 100644 --- a/daprdocs/content/en/getting-started/quickstarts/cryptography-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/cryptography-quickstart.md @@ -43,7 +43,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/cryptography) +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/cryptography/javascript/sdk) ```bash git clone https://github.com/dapr/quickstarts.git @@ -245,7 +245,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/cryptography) +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/cryptography/go/sdk) ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md index 963105186..8b52eedb1 100644 --- a/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/jobs-quickstart.md @@ -43,7 +43,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/jobs). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/jobs/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md index 82d588442..c8fae336f 100644 --- a/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/pubsub-quickstart.md @@ -39,7 +39,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -217,7 +217,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -365,7 +365,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -522,7 +522,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -683,7 +683,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -843,7 +843,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -1017,7 +1017,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -1175,7 +1175,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -1331,7 +1331,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -1493,7 +1493,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/pub_sub/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md index 92f926107..b2628e6bd 100644 --- a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-serviceinvo-quickstart.md @@ -32,7 +32,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/python/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -238,7 +238,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/javascript/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -468,7 +468,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/csharp/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -703,7 +703,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/java/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -933,7 +933,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/go/http). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md index 674cb5dae..470807093 100644 --- a/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/resiliency/resiliency-state-quickstart.md @@ -32,7 +32,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/resiliency). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -203,7 +203,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/resiliency). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -372,7 +372,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/resiliency). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -544,7 +544,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/resiliency). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -712,7 +712,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/resiliency). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md index de12598d2..12afadacc 100644 --- a/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/secrets-quickstart.md @@ -32,7 +32,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -141,7 +141,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management). +Clone the [sample provided in the Quickstarts repo](hhttps://github.com/dapr/quickstarts/tree/master/secrets_management/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -254,7 +254,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -366,7 +366,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -471,7 +471,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/secrets_management/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md index 0164e35fd..67d08db2a 100644 --- a/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/serviceinvocation-quickstart.md @@ -36,7 +36,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/python/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -182,7 +182,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/javascript/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -322,7 +322,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/csharp/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -468,7 +468,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/java/http). ```bash git clone https://github.com/dapr/quickstarts.git @@ -606,7 +606,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/service_invocation/go/http). ```bash diff --git a/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md index a0448f0d3..8e2a7d592 100644 --- a/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/statemanagement-quickstart.md @@ -34,7 +34,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -163,7 +163,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -295,7 +295,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -429,7 +429,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -562,7 +562,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](hhttps://github.com/dapr/quickstarts/tree/master/state_management/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -702,7 +702,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -818,7 +818,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -940,7 +940,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -1060,7 +1060,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -1179,7 +1179,7 @@ For this example, you will need: ### Step 1: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/state_management/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git diff --git a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md index f99d0ebd1..0129fabba 100644 --- a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md @@ -48,7 +48,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows/python/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -290,7 +290,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows/javascript/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -509,7 +509,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows/csharp/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -761,7 +761,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows/java/sdk). ```bash git clone https://github.com/dapr/quickstarts.git @@ -1100,7 +1100,7 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows/go/sdk). ```bash git clone https://github.com/dapr/quickstarts.git From a633c9c3edc7b0075d6181dd53b187ff5dd9c820 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 31 Jul 2024 10:14:20 -0400 Subject: [PATCH 67/92] fix broken links Signed-off-by: Hannah Hunter --- .../building-blocks/jobs/jobs-overview.md | 2 +- .../en/operations/observability/metrics/metrics-overview.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md index b4d5214eb..24ebf808b 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md @@ -32,7 +32,7 @@ The jobs API is a job scheduler, not the executor which runs the job. The design All job details and user-associated data for scheduled jobs are stored in an embedded Etcd database in the Scheduler service. You can use jobs to: -- **Delay your [pub/sub messaging]({<< ref pubsub-overview.md >>}).** You can publish a message in a future specific time (for example: a week from today, or a specific UTC date/time). +- **Delay your [pub/sub messaging]({{< ref pubsub-overview.md >}}).** You can publish a message in a future specific time (for example: a week from today, or a specific UTC date/time). - **Schedule [service invocation]({{< ref service-invocation-overview.md >}}) method calls between applications.** ## Scenarios diff --git a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md index cc7e606d3..5f07bb325 100644 --- a/daprdocs/content/en/operations/observability/metrics/metrics-overview.md +++ b/daprdocs/content/en/operations/observability/metrics/metrics-overview.md @@ -74,7 +74,7 @@ spec: When invoking Dapr using HTTP, metrics are created for each requested method by default. This can result in a high number of metrics, known as high cardinality, which can impact memory usage and CPU. -Path matching allows you to manage and control the cardinality of HTTP metrics in Dapr. This is an aggregation of metrics, so rather than having a metric for each event, you can reduce the number of metrics events and report an overall number. For details on how to set the cardinality in configuration see ({{< ref "configuration-overview.md#metrics" >}}) +Path matching allows you to manage and control the cardinality of HTTP metrics in Dapr. This is an aggregation of metrics, so rather than having a metric for each event, you can reduce the number of metrics events and report an overall number. [Learn more about how to set the cardinality in configuration]({{< ref "configuration-overview.md#metrics" >}}). This configuration is opt-in and is enabled via the Dapr configuration `spec.metrics.http.pathMatching`. When defined, it enables path matching, which standardizes specified paths for both metrics paths. This reduces the number of unique metrics paths, making metrics more manageable and reducing resource consumption in a controlled way. From 8d7ff41381bd8dbb5a26e6b2ae52f52d1cbd1794 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 31 Jul 2024 10:25:12 -0400 Subject: [PATCH 68/92] remove GA features from preview table Signed-off-by: Hannah Hunter --- .../content/en/operations/support/support-preview-features.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/daprdocs/content/en/operations/support/support-preview-features.md b/daprdocs/content/en/operations/support/support-preview-features.md index 61701dec8..943b35d0e 100644 --- a/daprdocs/content/en/operations/support/support-preview-features.md +++ b/daprdocs/content/en/operations/support/support-preview-features.md @@ -19,9 +19,7 @@ For CLI there is no explicit opt-in, just the version that this was first made a | **Multi-App Run for Kubernetes** | Configure multiple Dapr applications from a single configuration file and run from a single command on Kubernetes | `dapr run -k -f` | [Multi-App Run]({{< ref multi-app-dapr-run.md >}}) | v1.12 | | **Workflows** | Author workflows as code to automate and orchestrate tasks within your application, like messaging, state management, and failure handling | N/A | [Workflows concept]({{< ref "components-concept#workflows" >}})| v1.10 | | **Cryptography** | Encrypt or decrypt data without having to manage secrets keys | N/A | [Cryptography concept]({{< ref "components-concept#cryptography" >}})| v1.11 | -| **Service invocation for non-Dapr endpoints** | Allow the invocation of non-Dapr endpoints by Dapr using the [Service invocation API]({{< ref service_invocation_api.md >}}). Read ["How-To: Invoke Non-Dapr Endpoints using HTTP"]({{< ref howto-invoke-non-dapr-endpoints.md >}}) for more information. | N/A | [Service invocation API]({{< ref service_invocation_api.md >}}) | v1.11 | | **Actor State TTL** | Allow actors to save records to state stores with Time To Live (TTL) set to automatically clean up old data. In its current implementation, actor state with TTL may not be reflected correctly by clients, read [Actor State Transactions]({{< ref actors_api.md >}}) for more information. | `ActorStateTTL` | [Actor State Transactions]({{< ref actors_api.md >}}) | v1.11 | -| **Transactional Outbox** | Allows state operations for inserts and updates to be published to a configured pub/sub topic using a single transaction across the state store and the pub/sub | N/A | [Transactional Outbox Feature]({{< ref howto-outbox.md >}}) | v1.12 | | **Component Hot Reloading** | Allows for Dapr-loaded components to be "hot reloaded". A component spec is reloaded when it is created/updated/deleted in Kubernetes or on file when running in self-hosted mode. Ignores changes to actor state stores and workflow backends. | `HotReload`| [Hot Reloading]({{< ref components-concept.md >}}) | v1.13 | | **Subscription Hot Reloading** | Allows for declarative subscriptions to be "hot reloaded". A subscription is reloaded either when it is created/updated/deleted in Kubernetes, or on file in self-hosted mode. In-flight messages are unaffected when reloading. | `HotReload`| [Hot Reloading]({{< ref "subscription-methods.md#declarative-subscriptions" >}}) | v1.14 | | **Job actor reminders** | Whilst the [Scheduler service]({{< ref "concepts/dapr-services/scheduler.md" >}}) is deployed by default, job actor reminders (used for scheduling actor reminders) are enabled through a preview feature and needs a feature flag. | `SchedulerReminders`| [Job actor reminders]({{< ref "jobs-overview.md#actor-reminders" >}}) | v1.14 | From d55a09c6fc0e40bbcc02a141d144cca67c28668e Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 31 Jul 2024 11:36:51 -0400 Subject: [PATCH 69/92] update website version yml Signed-off-by: Hannah Hunter --- .github/workflows/website-v1-14.yml | 8 ++--- .github/workflows/website-v1-15.yml | 55 +++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/website-v1-15.yml diff --git a/.github/workflows/website-v1-14.yml b/.github/workflows/website-v1-14.yml index a32aa6d42..1495c5aa1 100644 --- a/.github/workflows/website-v1-14.yml +++ b/.github/workflows/website-v1-14.yml @@ -4,11 +4,11 @@ on: workflow_dispatch: push: branches: - - v1.14 + - v1.15 pull_request: types: [opened, synchronize, reopened, closed] branches: - - v1.14 + - v1.15 jobs: build_and_deploy_job: @@ -29,7 +29,7 @@ jobs: HUGO_ENV: production HUGO_VERSION: "0.100.2" with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_14 }} + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_15 }} repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) skip_deploy_on_missing_secrets: true action: "upload" @@ -50,6 +50,6 @@ jobs: id: closepullrequest uses: Azure/static-web-apps-deploy@v1 with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_14 }} + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_15 }} skip_deploy_on_missing_secrets: true action: "close" diff --git a/.github/workflows/website-v1-15.yml b/.github/workflows/website-v1-15.yml new file mode 100644 index 000000000..1495c5aa1 --- /dev/null +++ b/.github/workflows/website-v1-15.yml @@ -0,0 +1,55 @@ +name: Azure Static Web App v1.14 + +on: + workflow_dispatch: + push: + branches: + - v1.15 + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - v1.15 + +jobs: + build_and_deploy_job: + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') + runs-on: ubuntu-latest + name: Build and Deploy Job + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + - name: Setup Docsy + run: cd daprdocs && git submodule update --init --recursive && sudo npm install -D --save autoprefixer && sudo npm install -D --save postcss-cli + - name: Build And Deploy + id: builddeploy + uses: Azure/static-web-apps-deploy@v1 + env: + HUGO_ENV: production + HUGO_VERSION: "0.100.2" + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_15 }} + repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) + skip_deploy_on_missing_secrets: true + action: "upload" + ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### + # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig + app_location: "/daprdocs" # App source code path + api_location: "api" # Api source code path - optional + output_location: "public" # Built app content directory - optional + app_build_command: "git config --global --add safe.directory /github/workspace && hugo" + ###### End of Repository/Build Configurations ###### + + close_pull_request_job: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Close Pull Request Job + steps: + - name: Close Pull Request + id: closepullrequest + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_15 }} + skip_deploy_on_missing_secrets: true + action: "close" From e13c59e1d1259bd46ceeaabc1f3057d87c65d1c4 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 31 Jul 2024 11:38:14 -0400 Subject: [PATCH 70/92] remove v1.14 yml Signed-off-by: Hannah Hunter --- .github/workflows/website-v1-14.yml | 55 ----------------------------- 1 file changed, 55 deletions(-) delete mode 100644 .github/workflows/website-v1-14.yml diff --git a/.github/workflows/website-v1-14.yml b/.github/workflows/website-v1-14.yml deleted file mode 100644 index 1495c5aa1..000000000 --- a/.github/workflows/website-v1-14.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Azure Static Web App v1.14 - -on: - workflow_dispatch: - push: - branches: - - v1.15 - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - v1.15 - -jobs: - build_and_deploy_job: - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy Job - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - fetch-depth: 0 - - name: Setup Docsy - run: cd daprdocs && git submodule update --init --recursive && sudo npm install -D --save autoprefixer && sudo npm install -D --save postcss-cli - - name: Build And Deploy - id: builddeploy - uses: Azure/static-web-apps-deploy@v1 - env: - HUGO_ENV: production - HUGO_VERSION: "0.100.2" - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_15 }} - repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) - skip_deploy_on_missing_secrets: true - action: "upload" - ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### - # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig - app_location: "/daprdocs" # App source code path - api_location: "api" # Api source code path - optional - output_location: "public" # Built app content directory - optional - app_build_command: "git config --global --add safe.directory /github/workspace && hugo" - ###### End of Repository/Build Configurations ###### - - close_pull_request_job: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close Pull Request Job - steps: - - name: Close Pull Request - id: closepullrequest - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_V1_15 }} - skip_deploy_on_missing_secrets: true - action: "close" From 7e4b80185bac534b3921f54b5565ad7fa2403348 Mon Sep 17 00:00:00 2001 From: Yaron Schneider Date: Wed, 31 Jul 2024 08:42:50 -0700 Subject: [PATCH 71/92] Fix outbox projection http json (#4286) * fix outbox projection http json Signed-off-by: yaron2 * update outbox projection text Signed-off-by: yaron2 * Update daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md Signed-off-by: Mark Fussell --------- Signed-off-by: yaron2 Signed-off-by: Mark Fussell Co-authored-by: Mark Fussell --- .../state-management/howto-outbox.md | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md index a2facf91b..40261a1af 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md @@ -110,11 +110,11 @@ spec: ### Shape the outbox pattern message -You can override the outbox pattern message published to the pub/sub broker by setting a different message. This is done via a projected transaction payload, called `outboxProjections`, which is ignored when the state is written and is used as the outbox pattern message published to the user topic. +You can override the outbox pattern message published to the pub/sub broker by setting another transaction that is not be saved to the database and is explicitly mentioned as a projection. This transaction is added a metadata key named `outbox.projection` with a value set to `true`. When added to the state array saved in a transaction, this payload is ignored when the state is written and the data is used as the payload sent to the upstream subscriber. -To set the `outboxProjections` to `true`, the `key` values must match between the operation on the state store and the message projection. If the keys do not match, the whole transaction fails. +To use correctly, the `key` values must match between the operation on the state store and the message projection. If the keys do not match, the whole transaction fails. -If you have two or more `outboxProjections` for the same key, the first one defined is used and the others are ignored. +If you have two or more `outbox.projection` enabled state items for the same key, the first one defined is used and the others are ignored. [Learn more about default and custom CloudEvent messages.]({{< ref pubsub-cloudevents.md >}}) @@ -368,26 +368,33 @@ You can pass the message override using the following HTTP request: curl -X POST http://localhost:3500/v1.0/state/starwars/transaction \ -H "Content-Type: application/json" \ -d '{ - "operations": [ - { - "operation": "upsert", - "request": { - "key": "key1", - "value": "2" - } - }, - { - "operation": "upsert", - "request": { - "key": "key1" - "value: "3" - "metadata": { - "outboxProjection": "true" - } - } - } - ], - }' + "operations": [ + { + "operation": "upsert", + "request": { + "key": "order1", + "value": { + "orderId": "7hf8374s", + "type": "book", + "name": "The name of the wind" + } + } + }, + { + "operation": "upsert", + "request": { + "key": "order1", + "value": { + "orderId": "7hf8374s" + }, + "metadata": { + "outbox.projection": "true" + }, + "contentType": "application/json" + } + } + ] +}' ``` By setting the metadata item `"outbox.projection"` to `"true"` and making sure the `key` values match (`key1`): From 1d8fd27588dede5a309ea525b587be6d7715ac63 Mon Sep 17 00:00:00 2001 From: Arhell Date: Thu, 1 Aug 2024 00:42:13 +0300 Subject: [PATCH 72/92] update concepts folder link Signed-off-by: Arhell --- daprdocs/content/en/concepts/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/concepts/overview.md b/daprdocs/content/en/concepts/overview.md index 63c173890..db19e3929 100644 --- a/daprdocs/content/en/concepts/overview.md +++ b/daprdocs/content/en/concepts/overview.md @@ -138,7 +138,7 @@ Dapr can be used from any developer framework. Here are some that have been inte | [.NET]({{< ref dotnet >}}) | [ASP.NET Core](https://github.com/dapr/dotnet-sdk/tree/master/examples/AspNetCore) | Brings stateful routing controllers that respond to pub/sub events from other services. Can also take advantage of [ASP.NET Core gRPC Services](https://docs.microsoft.com/aspnet/core/grpc/). | [Java]({{< ref java >}}) | [Spring Boot](https://spring.io/) | Build Spring boot applications with Dapr APIs | [Python]({{< ref python >}}) | [Flask]({{< ref python-flask.md >}}) | Build Flask applications with Dapr APIs -| [JavaScript](https://github.com/dapr/js-sdk) | [Express](http://expressjs.com/) | Build Express applications with Dapr APIs +| [JavaScript](https://github.com/dapr/js-sdk) | [Express](https://expressjs.com/) | Build Express applications with Dapr APIs | [PHP]({{< ref php >}}) | | You can serve with Apache, Nginx, or Caddyserver. #### Integrations and extensions From a97270747400fa6a17e9df0f133320b0712313ce Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Thu, 1 Aug 2024 12:08:32 -0400 Subject: [PATCH 73/92] remove claim that workflow can use trace context Signed-off-by: Hannah Hunter --- .../building-blocks/workflow/workflow-architecture.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-architecture.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-architecture.md index da44ff6dc..06269e0ad 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-architecture.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-architecture.md @@ -51,7 +51,9 @@ If you're familiar with Dapr actors, you may notice a few differences in terms o The `durabletask-go` core used by the workflow engine writes distributed traces using Open Telemetry SDKs. These traces are captured automatically by the Dapr sidecar and exported to the configured Open Telemetry provider, such as Zipkin. -Each workflow instance managed by the engine is represented as one or more spans. There is a single parent span representing the full workflow execution and child spans for the various tasks, including spans for activity task execution and durable timers. Workflow activity code also has access to the trace context, allowing distributed trace context to flow to external services that are invoked by the workflow. +Each workflow instance managed by the engine is represented as one or more spans. There is a single parent span representing the full workflow execution and child spans for the various tasks, including spans for activity task execution and durable timers. + +> Workflow activity code currently **does not** have access to the trace context. ## Internal workflow actors From d0eff4ec48cff2464da3c68828334f0044e3f232 Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Fri, 2 Aug 2024 10:30:28 -0400 Subject: [PATCH 74/92] Updates to the maintainer contributing guide (#4291) * update steps for creating new docs site Signed-off-by: Hannah Hunter * Update daprdocs/content/en/contributing/docs-contrib/maintainer-guide.md Signed-off-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> --------- Signed-off-by: Hannah Hunter Signed-off-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Co-authored-by: Yaron Schneider --- .../docs-contrib/maintainer-guide.md | 165 ++++++++++-------- 1 file changed, 95 insertions(+), 70 deletions(-) diff --git a/daprdocs/content/en/contributing/docs-contrib/maintainer-guide.md b/daprdocs/content/en/contributing/docs-contrib/maintainer-guide.md index 15891b3e0..bc8a0233c 100644 --- a/daprdocs/content/en/contributing/docs-contrib/maintainer-guide.md +++ b/daprdocs/content/en/contributing/docs-contrib/maintainer-guide.md @@ -83,6 +83,7 @@ After upmerge, prepare the docs branches for the release. In two separate PRs, y - Archive the latest release. - Bring the preview/release branch as the current, live version of the docs. +- Create a new preview branch. #### Latest release @@ -193,79 +194,24 @@ These steps will prepare the upcoming release branch for promotion to latest rel | [v1.2](https://github.com/dapr/docs/tree/v1.2) (pre-release) | https://v1-2.docs.dapr.io/ | Pre-release documentation. Doc updates that are only applicable to v1.2+ go here. | ``` -1. In VS Code, search for any `v1.0` references and replace them with `v1.1` as applicable. +1. Update the `dapr-latest-version.html` shortcode partial to the new minor/patch version (in this example, `1.1.0` and `1.1`). 1. Commit the staged changes and push to your branch (`release_v1.1`). 1. Open a PR from `release/v1.1` to `v1.1`. 1. Have a docs maintainer or approver review. Wait to merge the PR until release. -### Create new website for future release +#### Future preview branch -Next, create a new website for the future Dapr release, which you point to from the latest website. To do this, you'll need to: +##### Create preview branch -- Deploy an Azure Static Web App. -- Configure DNS via request from CNCF. +1. In GitHub UI, select the branch drop-down menu and select **View all branches**. +1. Click **New branch**. +1. In **New branch name**, enter the preview branch version number. In this example, it would be `v1.2`. +1. Select **v1.1** as the source. +1. Click **Create new branch**. -These steps require authentication. +##### Configure preview branch -#### Deploy Azure Static Web App - -Deploy a new Azure Static Web App for the future Dapr release. For this example, we use v1.2 as the future release. - -{{% alert title="Important" color="primary" %}} -You need Microsoft employee access to create a new Azure Static Web App. -{{% /alert %}} - -1. Use Azure PIM to [elevate into the Owner role](https://eng.ms/docs/cloud-ai-platform/devdiv/devdiv-azure-service-dmitryr/azure-devex-philon/dapr/dapr/assets/azure) for the Dapr Prod subscription. -1. Navigate to the [docs-website](https://ms.portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/38875a89-0178-4f27-a141-dc6fc01f183d/resourceGroups/docs-website/overview) resource group. -1. Select **+ Create** and search for **Static Web App**. Select **Create**. -1. Enter in the following information: - - Subscription: `Dapr Prod` - - Resource Group: `docs-website` - - Name: `daprdocs-v1-2` - - Hosting Plan: `Free` - - Region: `West US 2` - - Source: `Other` -1. Select **Review + create**, and then deploy the static web app. -1. Wait for deployment, and navigate to the new static web app resource. -1. Select **Manage deployment token** and copy the value. -1. Navigate to the docs repo **Secrets management** page under **Settings** and create a new secret named `AZURE_STATIC_WEB_APPS_V1_2`, and provide the value of the deployment token. - -#### Configure DNS - -{{% alert title="Important" color="primary" %}} - This section can only be completed on a Secure Admin Workstation (SAW). If you do not have a SAW device, ask a team member with one to assist. - -{{% /alert %}} - -1. Ensure you are a member of the `DMAdaprweb` security group in IDWeb. -1. Navigate to [https://prod.msftdomains.com/dns/form?environment=0](https://prod.msftdomains.com/dns/form?environment=0) on a SAW -1. Enter the following details in the left-hand pane: - - Team Owning Alias: `DMAdaprweb` - - Business Justification/Notes: `Configuring DNS for new Dapr docs website` - - Environment: `Internet/Public-facing` - - Zone: `dapr.io` - - Action: `Add` - - Incident ID: Leave blank - -1. Back in the new static web app you just deployed, navigate to the **Custom domains** blade and select **+ Add** -1. Enter `v1-2.docs.dapr.io` under **Domain name**. Click **Next**. -1. Keep **Hostname record type** as `CNAME`, and copy the value of **Value**. -1. Back in the domain portal, enter the following information in the main pane: - - Name: `v1-2.docs` - - Type: `CNAME` - - Data: Value you just copied from the static web app - -1. Click **Submit** in the top right corner. -1. Wait for two emails: - - One saying your request was received. - - One saying the request was completed. -1. Back in the Azure Portal, click **Add**. You may need to click a couple times to account for DNS delay. -1. A TLS certificate is now generated for you and the DNS record is saved. This may take 2-3 minutes. -1. Navigate to `https://v1-2.docs.dapr.io` and verify a blank website loads correctly. - -### Configure future website branch - -1. Open VS Code to the Dapr docs repo. +1. In a terminal window, navigate to the `docs` repo. 1. Switch to the upcoming release branch (`v1.1`) and synchronize changes: ```bash @@ -339,15 +285,94 @@ You need Microsoft employee access to create a new Azure Static Web App. url = "https://v1-0.docs.dapr.io" ``` -1. Commit the staged changes and push to the v1.2 branch. -1. Navigate to the [docs Actions page](https://github.com/dapr/docs/actions) and make sure the build & release successfully completed. -1. Navigate to the new `https://v1-2.docs.dapr.io` website and verify that the new version is displayed. +1. Commit the staged changes and push to a new PR against the v1.2 branch. +1. Hold on merging the PR until after release and the other `v1.0` and `v1.1` PRs have been merged. + +### Create new website for future release + +Next, create a new website for the future Dapr release. To do this, you'll need to: + +- Deploy an Azure Static Web App. +- Configure DNS via request from CNCF. + +#### Prerequisites +- Docs maintainer status in the `dapr/docs` repo. +- Access to the active Dapr Azure Subscription with Contributor or Owner access to create resources. +- [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux&pivots=os-windows) installed on your machine. +- Your own fork of the [`dapr/docs` repo](https://github.com/dapr/docs) cloned to your machine. + +#### Deploy Azure Static Web App + +Deploy a new Azure Static Web App for the future Dapr release. For this example, we use v1.1 as the future release. + +1. In a terminal window, navigate to the `iac/swa` folder in the `dapr/docs` directory. + + ```bash + cd .github/iac/swa + ``` + +1. Log into Azure Developer CLI (`azd`) using the Dapr Azure subscription. + + ```bash + azd login + ``` + +1. In the browser prompt, verify you're logging in as Dapr and complete the login. + +1. In a new terminal, replace the following values with the website values you prefer. + + ```bash + export AZURE_RESOURCE_GROUP=rg-dapr-docs-test + export IDENTITY_RESOURCE_GROUP=rg-my-identities + export AZURE_STATICWEBSITE_NAME=daprdocs-latest + ``` + +1. Create a new [`azd` environment](https://learn.microsoft.com/azure/developer/azure-developer-cli/faq#what-is-an-environment-name). + + ```bash + azd env new + ``` + +1. When prompted, enter a new environment name. For this example, you'd name the environment something like: `dapr-docs-v1-1`. + +1. Once the environment is created, deploy the Dapr docs SWA into the new environment using the following command: + + ```bash + azd up + ``` + +1. When prompted, select an Azure subscription and location. Match these to the Dapr Azure subscription. + +#### Configure the SWA in the Azure portal + +Head over to the Dapr subscription in the [Azure portal](https://portal.azure.com) and verify that your new Dapr docs site has been deployed. + +Optionally, grant the correct minimal permissions for inbound publishing and outbound access to dependencies using the **Static Web App** > **Access control (IAM)** blade in the portal. + +#### Configure DNS + +1. In the Azure portal, from the new SWA you just created, naviage to **Custom domains** from the left side menu. +1. Copy the "CNAME" value of the web app. +1. Using your own account, [submit a CNCF ticket](https://jira.linuxfoundation.org/secure/Dashboard.jspa) to create a new domain name mapped to the CNAME value you copied. For this example, to create a new domain for Dapr v1.1, you'd request to map to `v1-1.docs.dapr.io`. + + Request resolution may take some time. + +1. Once the new domain has been confirmed, return to the static web app in the portal. +1. Navigate to the **Custom domains** blade and select **+ Add**. +1. Select **Custom domain on other DNS**. +1. Enter `v1-1.docs.dapr.io` under **Domain name**. Click **Next**. +1. Keep **Hostname record type** as `CNAME`, and copy the value of **Value**. +1. Click **Add**. +1. Navigate to `https://v1-1.docs.dapr.io` and verify a blank website loads correctly. + +You can repeat these steps for any preview versions. ### On the new Dapr release date 1. Wait for all code/containers/Helm charts to be published. -1. Merge the the PR from `release_v1.0` to `v1.0`. Delete the release/v1.0 branch. -1. Merge the the PR from `release_v1.1` to `v1.1`. Delete the release/v1.1 branch. +1. Merge the PR from `release_v1.0` to `v1.0`. Delete the release/v1.0 branch. +1. Merge the PR from `release_v1.1` to `v1.1`. Delete the release/v1.1 branch. +1. Merge the PR from `release_v1.2` to `v1.2`. Delete the release/v1.2 branch. Congrats on the new docs release! 🚀 🎉 🎈 From c12dd3ab915395f7fa435f2b60f607aea6188e75 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Fri, 2 Aug 2024 12:58:38 -0400 Subject: [PATCH 75/92] update release date Signed-off-by: Hannah Hunter --- .../content/en/operations/support/support-release-policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index 77a852159..e7d4844ea 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,7 +45,7 @@ The table below shows the versions of Dapr releases that have been tested togeth | Release date | Runtime | CLI | SDKs | Dashboard | Status | Release notes | |--------------------|:--------:|:--------|---------|---------|---------|------------| -| July 24th 2024 | 1.14.0
| 1.14.0 | Java 1.12.0
Go 1.11.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.1 | 0.15.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | +| August 2nd 2024 | 1.14.0
| 1.14.0 | Java 1.12.0
Go 1.11.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.1 | 0.15.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | | May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | | May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | | April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | From 5a6f6e8fc8349aea60274679b04c1853ce83ada2 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Fri, 2 Aug 2024 14:53:00 -0400 Subject: [PATCH 76/92] wrong date Signed-off-by: Hannah Hunter --- .../content/en/operations/support/support-release-policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index e7d4844ea..62e584ec5 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,7 +45,7 @@ The table below shows the versions of Dapr releases that have been tested togeth | Release date | Runtime | CLI | SDKs | Dashboard | Status | Release notes | |--------------------|:--------:|:--------|---------|---------|---------|------------| -| August 2nd 2024 | 1.14.0
| 1.14.0 | Java 1.12.0
Go 1.11.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.1 | 0.15.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | +| August 6th 2024 | 1.14.0
| 1.14.0 | Java 1.12.0
Go 1.11.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.1 | 0.15.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | | May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | | May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | | April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | From 958e42a3134bd29736a6afc46d72c2c82971e23b Mon Sep 17 00:00:00 2001 From: Arhell Date: Sat, 3 Aug 2024 00:35:38 +0300 Subject: [PATCH 77/92] update root folder links Signed-off-by: Arhell --- README.md | 4 ++-- .../docs/open-telemetry-collector/collector-config.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c8a4a8ada..322917881 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Dapr documentation -[![GitHub License](https://img.shields.io/github/license/dapr/docs?style=flat&label=License&logo=github)](https://github.com/dapr/docs/blob/v1.13/LICENSE) [![GitHub issue custom search in repo](https://img.shields.io/github/issues-search/dapr/docs?query=type%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22&label=Good%20first%20issues&style=flat&logo=github)](https://github.com/dapr/docs/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) [![Discord](https://img.shields.io/discord/778680217417809931?label=Discord&style=flat&logo=discord)](http://bit.ly/dapr-discord) [![YouTube Channel Views](https://img.shields.io/youtube/channel/views/UCtpSQ9BLB_3EXdWAUQYwnRA?style=flat&label=YouTube%20views&logo=youtube)](https://youtube.com/@daprdev) [![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/daprdev?logo=x&style=flat)](https://twitter.com/daprdev) +[![GitHub License](https://img.shields.io/github/license/dapr/docs?style=flat&label=License&logo=github)](https://github.com/dapr/docs/blob/v1.13/LICENSE) [![GitHub issue custom search in repo](https://img.shields.io/github/issues-search/dapr/docs?query=type%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22&label=Good%20first%20issues&style=flat&logo=github)](https://github.com/dapr/docs/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) [![Discord](https://img.shields.io/discord/778680217417809931?label=Discord&style=flat&logo=discord)](https://bit.ly/dapr-discord) [![YouTube Channel Views](https://img.shields.io/youtube/channel/views/UCtpSQ9BLB_3EXdWAUQYwnRA?style=flat&label=YouTube%20views&logo=youtube)](https://youtube.com/@daprdev) [![X (formerly Twitter) Follow](https://img.shields.io/twitter/follow/daprdev?logo=x&style=flat)](https://twitter.com/daprdev) If you are looking to explore the Dapr documentation, please go to the documentation website: @@ -23,7 +23,7 @@ For more information visit the [Dapr branch structure](https://docs.dapr.io/cont ## Contribution guidelines -Before making your first contribution, make sure to review the [contributing section](http://docs.dapr.io/contributing/) in the docs. +Before making your first contribution, make sure to review the [contributing section](https://docs.dapr.io/contributing/) in the docs. ## Overview diff --git a/daprdocs/static/docs/open-telemetry-collector/collector-config.yaml b/daprdocs/static/docs/open-telemetry-collector/collector-config.yaml index 78b37a928..970477b62 100644 --- a/daprdocs/static/docs/open-telemetry-collector/collector-config.yaml +++ b/daprdocs/static/docs/open-telemetry-collector/collector-config.yaml @@ -7,4 +7,4 @@ spec: tracing: samplingRate: "1" zipkin: - endpointAddress: "http://otel-collector.default.svc.cluster.local:9411/api/v2/spans" + endpointAddress: "https://otel-collector.default.svc.cluster.local:9411/api/v2/spans" From fe8da2c9c1d37607ab9487ad3979892e370946fd Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 5 Aug 2024 11:34:16 -0400 Subject: [PATCH 78/92] update dotnet example Signed-off-by: Hannah Hunter --- .../service-invocation/howto-invoke-discover-services.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md index 301c1542b..9466fd4d3 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md +++ b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md @@ -242,12 +242,10 @@ namespace EventService var orderId = random.Next(1,1000); //Using Dapr SDK to invoke a method - var order = new Order("1"); - var orderJson = JsonSerializer.Serialize(order); - var content = new StringContent(orderJson, Encoding.UTF8, "application/json"); + var order = new Order(orderId.ToString()); var httpClient = DaprClient.CreateInvokeHttpClient(); - var response = await httpClient.PostAsJsonAsync("http://order-processor/orders", content); + var response = await httpClient.PostAsJsonAsync("http://order-processor/orders", order); var result = await response.Content.ReadAsStringAsync(); Console.WriteLine("Order requested: " + orderId); From 75e6bd4ce2ca39ca8e23ce1548c428f7b75f1630 Mon Sep 17 00:00:00 2001 From: Joni Collinge Date: Mon, 5 Aug 2024 18:32:12 +0100 Subject: [PATCH 79/92] Add subscribe.v1alpha1 to the API allow list docs Signed-off-by: Joni Collinge --- daprdocs/content/en/operations/configuration/api-allowlist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/daprdocs/content/en/operations/configuration/api-allowlist.md b/daprdocs/content/en/operations/configuration/api-allowlist.md index 0ce0c55ba..0f2a42102 100644 --- a/daprdocs/content/en/operations/configuration/api-allowlist.md +++ b/daprdocs/content/en/operations/configuration/api-allowlist.md @@ -119,6 +119,7 @@ See this list of values corresponding to the different Dapr APIs: | [Service Invocation]({{< ref service_invocation_api.md >}}) | `invoke` (`v1.0`) | `invoke` (`v1`) | | [State]({{< ref state_api.md>}})| `state` (`v1.0` and `v1.0-alpha1`) | `state` (`v1` and `v1alpha1`) | | [Pub/Sub]({{< ref pubsub.md >}}) | `publish` (`v1.0` and `v1.0-alpha1`) | `publish` (`v1` and `v1alpha1`) | +| Subscribe | n/a | `subscribe` (`v1alpha1`) | | [(Output) Bindings]({{< ref bindings_api.md >}}) | `bindings` (`v1.0`) |`bindings` (`v1`) | | [Secrets]({{< ref secrets_api.md >}})| `secrets` (`v1.0`) | `secrets` (`v1`) | | [Actors]({{< ref actors_api.md >}}) | `actors` (`v1.0`) |`actors` (`v1`) | From 6d7e6abe8a1f982c0a45ec5a502d03e34bfe8d51 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Mon, 5 Aug 2024 14:41:06 -0400 Subject: [PATCH 80/92] update component spec for redis Signed-off-by: Hannah Hunter --- .../components-reference/supported-bindings/redis.md | 2 +- .../redis-configuration-store.md | 2 +- .../components-reference/supported-locks/redis-lock.md | 4 ++-- .../supported-pubsub/setup-redis-pubsub.md | 2 +- .../supported-state-stores/setup-redis.md | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/redis.md b/daprdocs/content/en/reference/components-reference/supported-bindings/redis.md index e147d101c..a87ccf7b6 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/redis.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/redis.md @@ -37,7 +37,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | Field | Required | Binding support | Details | Example | |--------------------|:--------:|------------|-----|---------| | `redisHost` | Y | Output | The Redis host address | `"localhost:6379"` | -| `redisPassword` | Y | Output | The Redis password | `"password"` | +| `redisPassword` | N | Output | The Redis password | `"password"` | | `redisUsername` | N | Output | Username for Redis host. Defaults to empty. Make sure your redis server version is 6 or above, and have created acl rule correctly. | `"username"` | | `enableTLS` | N | Output | If the Redis instance supports TLS with public certificates it can be configured to enable or disable TLS. Defaults to `"false"` | `"true"`, `"false"` | | `failover` | N | Output | Property to enabled failover configuration. Needs sentinalMasterName to be set. Defaults to `"false"` | `"true"`, `"false"` diff --git a/daprdocs/content/en/reference/components-reference/supported-configuration-stores/redis-configuration-store.md b/daprdocs/content/en/reference/components-reference/supported-configuration-stores/redis-configuration-store.md index 205cc98ad..e267d1d39 100644 --- a/daprdocs/content/en/reference/components-reference/supported-configuration-stores/redis-configuration-store.md +++ b/daprdocs/content/en/reference/components-reference/supported-configuration-stores/redis-configuration-store.md @@ -39,7 +39,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | Field | Required | Details | Example | |--------------------|:--------:|---------|---------| | redisHost | Y | Output | The Redis host address | `"localhost:6379"` | -| redisPassword | Y | Output | The Redis password | `"password"` | +| redisPassword | N | Output | The Redis password | `"password"` | | redisUsername | N | Output | Username for Redis host. Defaults to empty. Make sure your Redis server version is 6 or above, and have created acl rule correctly. | `"username"` | | enableTLS | N | Output | If the Redis instance supports TLS with public certificates it can be configured to enable or disable TLS. Defaults to `"false"` | `"true"`, `"false"` | | failover | N | Output | Property to enabled failover configuration. Needs sentinelMasterName to be set. Defaults to `"false"` | `"true"`, `"false"` diff --git a/daprdocs/content/en/reference/components-reference/supported-locks/redis-lock.md b/daprdocs/content/en/reference/components-reference/supported-locks/redis-lock.md index 7c39c9f6e..a51b1bc56 100644 --- a/daprdocs/content/en/reference/components-reference/supported-locks/redis-lock.md +++ b/daprdocs/content/en/reference/components-reference/supported-locks/redis-lock.md @@ -20,7 +20,7 @@ spec: metadata: - name: redisHost value: - - name: redisPassword + - name: redisPassword #Optional. value: - name: enableTLS value: # Optional. Allowed: true, false. @@ -80,7 +80,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | Field | Required | Details | Example | |--------------------|:--------:|---------|---------| | redisHost | Y | Connection-string for the redis host | `localhost:6379`, `redis-master.default.svc.cluster.local:6379` -| redisPassword | Y | Password for Redis host. No Default. Can be `secretKeyRef` to use a secret reference | `""`, `"KeFg23!"` +| redisPassword | N | Password for Redis host. No Default. Can be `secretKeyRef` to use a secret reference | `""`, `"KeFg23!"` | redisUsername | N | Username for Redis host. Defaults to empty. Make sure your redis server version is 6 or above, and have created acl rule correctly. | `""`, `"default"` | enableTLS | N | If the Redis instance supports TLS with public certificates, can be configured to be enabled or disabled. Defaults to `"false"` | `"true"`, `"false"` | maxRetries | N | Maximum number of retries before giving up. Defaults to `3` | `5`, `10` diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-redis-pubsub.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-redis-pubsub.md index 6a7ee1d39..af9682e8e 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-redis-pubsub.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-redis-pubsub.md @@ -39,7 +39,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | Field | Required | Details | Example | |--------------------|:--------:|---------|---------| | redisHost | Y | Connection-string for the redis host. If `"redisType"` is `"cluster"` it can be multiple hosts separated by commas or just a single host | `localhost:6379`, `redis-master.default.svc.cluster.local:6379` -| redisPassword | Y | Password for Redis host. No Default. Can be `secretKeyRef` to use a secret reference | `""`, `"KeFg23!"` +| redisPassword | N | Password for Redis host. No Default. Can be `secretKeyRef` to use a secret reference | `""`, `"KeFg23!"` | redisUsername | N | Username for Redis host. Defaults to empty. Make sure your redis server version is 6 or above, and have created acl rule correctly. | `""`, `"default"` | consumerID | N | The consumer group ID. | Can be set to string value (such as `"channel1"` in the example above) or string format value (such as `"{podName}"`, etc.). [See all of template tags you can use in your component metadata.]({{< ref "component-schema.md#templated-metadata-values" >}}) | enableTLS | N | If the Redis instance supports TLS with public certificates, can be configured to be enabled or disabled. Defaults to `"false"` | `"true"`, `"false"` diff --git a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-redis.md b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-redis.md index 61d5de0f3..dd273292d 100644 --- a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-redis.md +++ b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-redis.md @@ -26,7 +26,7 @@ spec: metadata: - name: redisHost value: - - name: redisPassword + - name: redisPassword # Optional. value: - name: enableTLS value: # Optional. Allowed: true, false. @@ -96,7 +96,7 @@ If you wish to use Redis as an actor store, append the following to the yaml. | Field | Required | Details | Example | |--------------------|:--------:|---------|---------| | redisHost | Y | Connection-string for the redis host | `localhost:6379`, `redis-master.default.svc.cluster.local:6379` -| redisPassword | Y | Password for Redis host. No Default. Can be `secretKeyRef` to use a secret reference | `""`, `"KeFg23!"` +| redisPassword | N | Password for Redis host. No Default. Can be `secretKeyRef` to use a secret reference | `""`, `"KeFg23!"` | redisUsername | N | Username for Redis host. Defaults to empty. Make sure your redis server version is 6 or above, and have created acl rule correctly. | `""`, `"default"` | enableTLS | N | If the Redis instance supports TLS with public certificates, can be configured to be enabled or disabled. Defaults to `"false"` | `"true"`, `"false"` | maxRetries | N | Maximum number of retries before giving up. Defaults to `3` | `5`, `10` From 9afc2759c3fc43242d09026d84d1fc038a75f468 Mon Sep 17 00:00:00 2001 From: Joni Collinge Date: Wed, 7 Aug 2024 16:39:34 +0100 Subject: [PATCH 81/92] Change grpc api link Signed-off-by: Joni Collinge --- daprdocs/content/en/operations/configuration/api-allowlist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/operations/configuration/api-allowlist.md b/daprdocs/content/en/operations/configuration/api-allowlist.md index 0f2a42102..75930dba8 100644 --- a/daprdocs/content/en/operations/configuration/api-allowlist.md +++ b/daprdocs/content/en/operations/configuration/api-allowlist.md @@ -114,7 +114,7 @@ The `name` field takes the name of the Dapr API you would like to enable. See this list of values corresponding to the different Dapr APIs: -| API group | HTTP API | [gRPC API](https://github.com/dapr/dapr/blob/master/pkg/grpc/endpoints.go) | +| API group | HTTP API | [gRPC API](https://github.com/dapr/dapr/tree/master/pkg/api/grpc) | | ----- | ----- | ----- | | [Service Invocation]({{< ref service_invocation_api.md >}}) | `invoke` (`v1.0`) | `invoke` (`v1`) | | [State]({{< ref state_api.md>}})| `state` (`v1.0` and `v1.0-alpha1`) | `state` (`v1` and `v1alpha1`) | From 914863f13d065a5af40a1f5c7907a61841df73e9 Mon Sep 17 00:00:00 2001 From: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:05:50 -0400 Subject: [PATCH 82/92] pull in update to dotnet docs (#4302) Signed-off-by: Hannah Hunter --- sdkdocs/dotnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdkdocs/dotnet b/sdkdocs/dotnet index 56367963f..b8e276728 160000 --- a/sdkdocs/dotnet +++ b/sdkdocs/dotnet @@ -1 +1 @@ -Subproject commit 56367963f46257fbcb109f671ac78dc445435012 +Subproject commit b8e276728935c66b0a335b5aa2ca4102c560dd3d From 9bb20a0df7ae72c857e32818b89186c6f283aaba Mon Sep 17 00:00:00 2001 From: Josh van Leeuwen Date: Tue, 13 Aug 2024 13:54:08 -0500 Subject: [PATCH 83/92] Adds docs on using Scheduler with Ephemeral storage (#4306) Signed-off-by: joshvanl --- .../building-blocks/jobs/jobs-overview.md | 5 -- .../kubernetes-persisting-scheduler.md | 49 ++++++++++++++++--- daprdocs/content/en/reference/api/jobs_api.md | 5 -- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md index 24ebf808b..486cfc5d6 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/jobs/jobs-overview.md @@ -16,11 +16,6 @@ Jobs in Dapr consist of: [See example scenarios.]({{< ref "#scenarios" >}}) -{{% alert title="Warning" color="warning" %}} -By default, job data is not resilient to [Scheduler]({{< ref scheduler.md >}}) service restarts. -A persistent volume must be provided to Scheduler to ensure job data is not lost in either [Kubernetes]({{< ref kubernetes-persisting-scheduler.md >}}) or [Self-hosted]({{< ref self-hosted-persisting-scheduler.md >}}) mode. -{{% /alert %}} - Diagram showing the Scheduler control plane service and the jobs API ## How it works diff --git a/daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md b/daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md index 130122192..9172a28fe 100644 --- a/daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md +++ b/daprdocs/content/en/operations/hosting/kubernetes/kubernetes-persisting-scheduler.md @@ -6,12 +6,15 @@ weight: 50000 description: "Configure Scheduler to persist its database to make it resilient to restarts" --- -The [Scheduler]({{< ref scheduler.md >}}) service is responsible for writing jobs to its embedded database and scheduling them for execution. -By default, the Scheduler service database writes this data to an in-memory ephemeral tempfs volume, meaning that **this data is not persisted across restarts**. Job data will be lost during these events. +The [Scheduler]({{< ref scheduler.md >}}) service is responsible for writing jobs to its embedded Etcd database and scheduling them for execution. +By default, the Scheduler service database writes this data to a Persistent Volume Claim of 1Gb of size using the cluster's default [storage class](https://kubernetes.io/docs/concepts/storage/storage-classes/). This means that there is no additional parameter required to run the scheduler service reliably on most Kubernetes deployments, although you will need additional configuration in some deployments or for a production environment. -To make the Scheduler data resilient to restarts, a persistent volume must be mounted to the Scheduler `StatefulSet`. -This persistent volume is backed by a real disk that is provided by the hosted Cloud Provider or Kubernetes infrastructure platform. -Disk size is determined by how many jobs are expected to be persisted at once; however, 64Gb should be more than sufficient for most use cases. +## Production Setup + +In case your Kubernetes deployment does not have a default storage class or you are configuring a production cluster, defining a storage class is required. + +A persistent volume is backed by a real disk that is provided by the hosted Cloud Provider or Kubernetes infrastructure platform. +Disk size is determined by how many jobs are expected to be persisted at once; however, 64Gb should be more than sufficient for most production scenarios. Some Kubernetes providers recommend using a [CSI driver](https://kubernetes.io/docs/concepts/storage/volumes/#csi) to provision the underlying disks. Below are a list of useful links to the relevant documentation for creating a persistent disk for the major cloud providers: - [Google Cloud Persistent Disk](https://cloud.google.com/compute/docs/disks) @@ -23,7 +26,7 @@ Below are a list of useful links to the relevant documentation for creating a pe - [Alibaba Cloud Disk Storage](https://www.alibabacloud.com/help/ack/ack-managed-and-ack-dedicated/user-guide/create-a-pvc) -Once the persistent volume class is available, you can install Dapr using the following command, with Scheduler configured to use the persistent volume class (replace `my-storage-class` with the name of the storage class): +Once the storage class is available, you can install Dapr using the following command, with Scheduler configured to use the storage class (replace `my-storage-class` with the name of the storage class): {{% alert title="Note" color="primary" %}} If Dapr is already installed, the control plane needs to be completely [uninstalled]({{< ref dapr-uninstall.md >}}) in order for the Scheduler `StatefulSet` to be recreated with the new persistent volume. @@ -53,3 +56,37 @@ helm upgrade --install dapr dapr/dapr \ {{% /codetab %}} {{< /tabs >}} + +## Ephemeral Storage + +Scheduler can be optionally made to use Ephemeral storage, which is in-memory storage which is **not** resilient to restarts, i.e. all Job data will be lost after a Scheduler restart. +This is useful for deployments where storage is not available or required, or for testing purposes. + +{{% alert title="Note" color="primary" %}} +If Dapr is already installed, the control plane needs to be completely [uninstalled]({{< ref dapr-uninstall.md >}}) in order for the Scheduler `StatefulSet` to be recreated without the persistent volume. +{{% /alert %}} + +{{< tabs "Dapr CLI" "Helm" >}} + +{{% codetab %}} + +```bash +dapr init -k --set dapr_scheduler.cluster.inMemoryStorage=true +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```bash +helm upgrade --install dapr dapr/dapr \ +--version={{% dapr-latest-version short="true" %}} \ +--namespace dapr-system \ +--create-namespace \ +--set dapr_scheduler.cluster.inMemoryStorage=true \ +--wait +``` + +{{% /codetab %}} +{{< /tabs >}} diff --git a/daprdocs/content/en/reference/api/jobs_api.md b/daprdocs/content/en/reference/api/jobs_api.md index 37332b848..3a04ed1a9 100644 --- a/daprdocs/content/en/reference/api/jobs_api.md +++ b/daprdocs/content/en/reference/api/jobs_api.md @@ -10,11 +10,6 @@ weight: 1300 The jobs API is currently in alpha. {{% /alert %}} -{{% alert title="Warning" color="warning" %}} -By default, job data is not resilient to [Scheduler]({{< ref scheduler.md >}}) service restarts. -A persistent volume must be provided to Scheduler to ensure job data is not lost in either [Kubernetes]({{< ref kubernetes-persisting-scheduler.md >}}) or [Self-Hosted]({{< ref self-hosted-persisting-scheduler.md >}}) mode. -{{% /alert %}} - With the jobs API, you can schedule jobs and tasks in the future. > The HTTP APIs are intended for development and testing only. For production scenarios, the use of the SDKs is strongly From 0f3729553caa1f76510b2186a038dc7f359b5cec Mon Sep 17 00:00:00 2001 From: Cassie Coyle Date: Tue, 13 Aug 2024 19:33:46 -0500 Subject: [PATCH 84/92] add ackDeadline to list (#4307) Signed-off-by: Cassandra Coyle --- .../components-reference/supported-pubsub/setup-gcp-pubsub.md | 1 + 1 file changed, 1 insertion(+) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-gcp-pubsub.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-gcp-pubsub.md index ca2f6e7e4..088126b1c 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-gcp-pubsub.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-gcp-pubsub.md @@ -99,6 +99,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | maxOutstandingMessages | N | Maximum number of outstanding messages a given [streaming-pull](https://cloud.google.com/pubsub/docs/pull#streamingpull_api) connection can have. Default: `1000` | `50` | maxOutstandingBytes | N | Maximum number of outstanding bytes a given [streaming-pull](https://cloud.google.com/pubsub/docs/pull#streamingpull_api) connection can have. Default: `1000000000` | `1000000000` | maxConcurrentConnections | N | Maximum number of concurrent [streaming-pull](https://cloud.google.com/pubsub/docs/pull#streamingpull_api) connections to be maintained. Default: `10` | `2` +| ackDeadline | N | Message acknowledgement duration deadline. Default: `20s` | `1m` From 6c1c863cf559fa00f8549a1855243430e324c4c3 Mon Sep 17 00:00:00 2001 From: Artur Souza Date: Tue, 13 Aug 2024 19:32:43 -0700 Subject: [PATCH 85/92] Document adding ApplicationProperties to ASB message Signed-off-by: Artur Souza --- .../components-reference/supported-bindings/servicebusqueues.md | 2 ++ .../supported-pubsub/setup-azure-servicebus-queues.md | 2 ++ .../supported-pubsub/setup-azure-servicebus-topics.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md b/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md index c836626ed..a6a9f60e9 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md @@ -166,6 +166,8 @@ In addition to the [settable metadata listed above](#sending-a-message-with-meta To find out more details on the purpose of any of these metadata properties, please refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). +In addition, all entries of `ApplicationProperties` from the original Azure Service Bus message are appended as `metadata.`. + {{% alert title="Note" color="primary" %}} All times are populated by the server and are not adjusted for clock skews. {{% /alert %}} diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index 2bc7ca5f9..3facbaa47 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -153,6 +153,8 @@ In addition to the [settable metadata listed above](#sending-a-message-with-meta To find out more details on the purpose of any of these metadata properties, please refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). +In addition, all entries of `ApplicationProperties` from the original Azure Service Bus message are appended as `metadata.`. + {{% alert title="Note" color="primary" %}} All times are populated by the server and are not adjusted for clock skews. {{% /alert %}} diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md index 78b73137f..94d665a59 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md @@ -157,6 +157,8 @@ In addition to the [settable metadata listed above](#sending-a-message-with-meta To find out more details on the purpose of any of these metadata properties, please refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). +In addition, all entries of `ApplicationProperties` from the original Azure Service Bus message are appended as `metadata.`. + > Note: that all times are populated by the server and are not adjusted for clock skews. ## Subscribe to a session enabled topic From 1e3065241160245fb830deb143724f7882447217 Mon Sep 17 00:00:00 2001 From: Mark Fussell Date: Tue, 13 Aug 2024 21:19:50 -0700 Subject: [PATCH 86/92] Update daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md Signed-off-by: Mark Fussell --- .../components-reference/supported-bindings/servicebusqueues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md b/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md index a6a9f60e9..d464e8b64 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md @@ -164,7 +164,7 @@ In addition to the [settable metadata listed above](#sending-a-message-with-meta - `metadata.EnqueuedTimeUtc` - `metadata.SequenceNumber` -To find out more details on the purpose of any of these metadata properties, please refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). +To find out more details on the purpose of any of these metadata properties refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). In addition, all entries of `ApplicationProperties` from the original Azure Service Bus message are appended as `metadata.`. From d34bebbd74d5279433e59ab1661bbf9f6cb2c630 Mon Sep 17 00:00:00 2001 From: Mark Fussell Date: Tue, 13 Aug 2024 21:19:55 -0700 Subject: [PATCH 87/92] Update daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md Signed-off-by: Mark Fussell --- .../supported-pubsub/setup-azure-servicebus-queues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index 3facbaa47..8291bd50d 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -151,7 +151,7 @@ In addition to the [settable metadata listed above](#sending-a-message-with-meta - `metadata.EnqueuedTimeUtc` - `metadata.SequenceNumber` -To find out more details on the purpose of any of these metadata properties, please refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). +To find out more details on the purpose of any of these metadata properties refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). In addition, all entries of `ApplicationProperties` from the original Azure Service Bus message are appended as `metadata.`. From 522524fdb400fda38e3bc61829ff6d7495350bb2 Mon Sep 17 00:00:00 2001 From: Mark Fussell Date: Tue, 13 Aug 2024 21:20:00 -0700 Subject: [PATCH 88/92] Update daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md Signed-off-by: Mark Fussell --- .../supported-pubsub/setup-azure-servicebus-topics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md index 94d665a59..a4f7bdb45 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md @@ -155,7 +155,7 @@ In addition to the [settable metadata listed above](#sending-a-message-with-meta - `metadata.EnqueuedTimeUtc` - `metadata.SequenceNumber` -To find out more details on the purpose of any of these metadata properties, please refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). +To find out more details on the purpose of any of these metadata properties refer to [the official Azure Service Bus documentation](https://docs.microsoft.com/rest/api/servicebus/message-headers-and-properties#message-headers). In addition, all entries of `ApplicationProperties` from the original Azure Service Bus message are appended as `metadata.`. From 7a0fab7f56326cf260d6429acc8b570ff9d3c64e Mon Sep 17 00:00:00 2001 From: Artur Souza Date: Tue, 13 Aug 2024 21:22:54 -0700 Subject: [PATCH 89/92] Update AutoDeleteOnIdleInSec doc to mention value >= 300s (#4309) Signed-off-by: Artur Souza --- .../components-reference/supported-bindings/servicebusqueues.md | 2 +- .../supported-pubsub/setup-azure-servicebus-queues.md | 2 +- .../supported-pubsub/setup-azure-servicebus-topics.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md b/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md index c836626ed..7696e3159 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/servicebusqueues.md @@ -73,7 +73,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | `namespaceName`| N | Input/Output | Parameter to set the address of the Service Bus namespace, as a fully-qualified domain name. Required if using Microsoft Entra ID authentication. | `"namespace.servicebus.windows.net"` | | `disableEntityManagement` | N | Input/Output | When set to true, queues and subscriptions do not get created automatically. Default: `"false"` | `"true"`, `"false"` | `lockDurationInSec` | N | Input/Output | Defines the length in seconds that a message will be locked for before expiring. Used during subscription creation only. Default set by server. | `"30"` -| `autoDeleteOnIdleInSec` | N | Input/Output | Time in seconds to wait before auto deleting idle subscriptions. Used during subscription creation only. Default: `"0"` (disabled) | `"3600"` +| `autoDeleteOnIdleInSec` | N | Input/Output | Time in seconds to wait before auto deleting idle subscriptions. Used during subscription creation only. Must be 300s or greater. Default: `"0"` (disabled) | `"3600"` | `defaultMessageTimeToLiveInSec` | N | Input/Output | Default message time to live, in seconds. Used during subscription creation only. | `"10"` | `maxDeliveryCount` | N | Input/Output | Defines the number of attempts the server will make to deliver a message. Used during subscription creation only. Default set by server. | `"10"` | `minConnectionRecoveryInSec` | N | Input/Output | Minimum interval (in seconds) to wait before attempting to reconnect to Azure Service Bus in case of a connection failure. Default: `"2"` | `"5"` diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md index 2bc7ca5f9..b69ef40b5 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-queues.md @@ -80,7 +80,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | `maxConcurrentHandlers` | N | Defines the maximum number of concurrent message handlers. Default: `0` (unlimited) | `10` | `disableEntityManagement` | N | When set to true, queues and subscriptions do not get created automatically. Default: `"false"` | `"true"`, `"false"` | `defaultMessageTimeToLiveInSec` | N | Default message time to live, in seconds. Used during subscription creation only. | `10` -| `autoDeleteOnIdleInSec` | N | Time in seconds to wait before auto deleting idle subscriptions. Used during subscription creation only. Default: `0` (disabled) | `3600` +| `autoDeleteOnIdleInSec` | N | Time in seconds to wait before auto deleting idle subscriptions. Used during subscription creation only. Must be 300s or greater. Default: `0` (disabled) | `3600` | `maxDeliveryCount` | N | Defines the number of attempts the server will make to deliver a message. Used during subscription creation only. Default set by server. | `10` | `lockDurationInSec` | N | Defines the length in seconds that a message will be locked for before expiring. Used during subscription creation only. Default set by server. | `30` | `minConnectionRecoveryInSec` | N | Minimum interval (in seconds) to wait before attempting to reconnect to Azure Service Bus in case of a connection failure. Default: `2` | `5` diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md index 78b73137f..4659b2e8c 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-azure-servicebus-topics.md @@ -84,7 +84,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | `disableEntityManagement` | N | When set to true, queues and subscriptions do not get created automatically. Default: `"false"` | `"true"`, `"false"` | `defaultMessageTimeToLiveInSec` | N | Default message time to live, in seconds. Used during subscription creation only. | `10` | `autoDeleteOnIdleInSec` | N | Time in seconds to wait before auto deleting idle subscriptions. Used during subscription creation only. Default: `0` (disabled) | `3600` -| `maxDeliveryCount` | N | Defines the number of attempts the server will make to deliver a message. Used during subscription creation only. Default set by server. | `10` +| `maxDeliveryCount` | N | Defines the number of attempts the server will make to deliver a message. Used during subscription creation only. Must be 300s or greater. Default set by server. | `10` | `lockDurationInSec` | N | Defines the length in seconds that a message will be locked for before expiring. Used during subscription creation only. Default set by server. | `30` | `minConnectionRecoveryInSec` | N | Minimum interval (in seconds) to wait before attempting to reconnect to Azure Service Bus in case of a connection failure. Default: `2` | `5` | `maxConnectionRecoveryInSec` | N | Maximum interval (in seconds) to wait before attempting to reconnect to Azure Service Bus in case of a connection failure. After each attempt, the component waits a random number of seconds, increasing every time, between the minimum and the maximum. Default: `300` (5 minutes) | `600` From bf79c1e9d83262dde50bac001cf08f4e1796c4e3 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 14 Aug 2024 11:43:54 -0400 Subject: [PATCH 90/92] update support policy doc Signed-off-by: Hannah Hunter --- .../support/support-release-policy.md | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index 62e584ec5..76a1a5730 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,24 +45,24 @@ The table below shows the versions of Dapr releases that have been tested togeth | Release date | Runtime | CLI | SDKs | Dashboard | Status | Release notes | |--------------------|:--------:|:--------|---------|---------|---------|------------| -| August 6th 2024 | 1.14.0
| 1.14.0 | Java 1.12.0
Go 1.11.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.1 | 0.15.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | -| May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | -| May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | -| April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | -| March 26th 2024 | 1.13.1
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.1 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.1) | -| March 6th 2024 | 1.13.0
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported (current) | [v1.13.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.0) | -| January 17th 2024 | 1.12.4
| 1.12.0 | Java 1.10.0
Go 1.9.1
PHP 1.2.0
Python 1.12.0
.NET 1.12.0
JS 3.2.0 | 0.14.0 | Supported (current) | [v1.12.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.4) | -| January 2nd 2024 | 1.12.3
| 1.12.0 | Java 1.10.0
Go 1.9.1
PHP 1.2.0
Python 1.12.0
.NET 1.12.0
JS 3.2.0 | 0.14.0 | Supported (current) | [v1.12.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.3) | -| November 18th 2023 | 1.12.2
| 1.12.0 | Java 1.10.0
Go 1.9.1
PHP 1.2.0
Python 1.12.0
.NET 1.12.0
JS 3.2.0 | 0.14.0 | Supported (current) | [v1.12.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.2) | +| August 14th 2024 | 1.14.0
| 1.14.0 | Java 1.12.0
Go 1.11.0
PHP 1.2.0
Python 1.14.0
.NET 1.14.0
JS 3.3.1 | 0.15.0 | Supported (current) | [v1.14.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.14.0) | +| May 29th 2024 | 1.13.4
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported | [v1.13.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.4) | +| May 21st 2024 | 1.13.3
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported | [v1.13.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.3) | +| April 3rd 2024 | 1.13.2
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported | [v1.13.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.2) | +| March 26th 2024 | 1.13.1
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported | [v1.13.1 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.1) | +| March 6th 2024 | 1.13.0
| 1.13.0 | Java 1.11.0
Go 1.10.0
PHP 1.2.0
Python 1.13.0
.NET 1.13.0
JS 3.3.0 | 0.14.0 | Supported | [v1.13.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.13.0) | +| January 17th 2024 | 1.12.4
| 1.12.0 | Java 1.10.0
Go 1.9.1
PHP 1.2.0
Python 1.12.0
.NET 1.12.0
JS 3.2.0 | 0.14.0 | Supported | [v1.12.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.4) | +| January 2nd 2024 | 1.12.3
| 1.12.0 | Java 1.10.0
Go 1.9.1
PHP 1.2.0
Python 1.12.0
.NET 1.12.0
JS 3.2.0 | 0.14.0 | Supported | [v1.12.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.3) | +| November 18th 2023 | 1.12.2
| 1.12.0 | Java 1.10.0
Go 1.9.1
PHP 1.2.0
Python 1.12.0
.NET 1.12.0
JS 3.2.0 | 0.14.0 | Supported | [v1.12.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.2) | | November 16th 2023 | 1.12.1
| 1.12.0 | Java 1.10.0
Go 1.9.1
PHP 1.2.0
Python 1.12.0
.NET 1.12.0
JS 3.2.0 | 0.14.0 | Supported | [v1.12.1 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.1) | | October 11th 2023 | 1.12.0
| 1.12.0 | Java 1.10.0
Go 1.9.0
PHP 1.1.0
Python 1.11.0
.NET 1.12.0
JS 3.1.2 | 0.14.0 | Supported | [v1.12.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.12.0) | -| November 18th 2023 | 1.11.6
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Supported | [v1.11.6 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.6) | -| November 3rd 2023 | 1.11.5
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Supported | [v1.11.5 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.5) | -| October 5th 2023 | 1.11.4
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Supported | [v1.11.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.4) | -| August 31st 2023 | 1.11.3
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Supported | [v1.11.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.3) | -| July 20th 2023 | 1.11.2
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Supported | [v1.11.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.2) | -| June 22nd 2023 | 1.11.1
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Supported | [v1.11.1 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.1) | -| June 12th 2023 | 1.11.0
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Supported | [v1.11.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.0) | +| November 18th 2023 | 1.11.6
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Unsupported | [v1.11.6 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.6) | +| November 3rd 2023 | 1.11.5
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Unsupported | [v1.11.5 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.5) | +| October 5th 2023 | 1.11.4
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Unsupported | [v1.11.4 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.4) | +| August 31st 2023 | 1.11.3
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Unsupported | [v1.11.3 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.3) | +| July 20th 2023 | 1.11.2
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Unsupported | [v1.11.2 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.2) | +| June 22nd 2023 | 1.11.1
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Unsupported | [v1.11.1 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.1) | +| June 12th 2023 | 1.11.0
| 1.11.0 | Java 1.9.0
Go 1.8.0
PHP 1.1.0
Python 1.10.0
.NET 1.11.0
JS 3.1.0 | 0.13.0 | Unsupported | [v1.11.0 release notes](https://github.com/dapr/dapr/releases/tag/v1.11.0) | | November 18th 2023 | 1.10.10
| 1.10.0 | Java 1.8.0
Go 1.7.0
PHP 1.1.0
Python 1.9.0
.NET 1.10.0
JS 3.0.0 | 0.11.0 | Unsupported | | | July 20th 2023 | 1.10.9
| 1.10.0 | Java 1.8.0
Go 1.7.0
PHP 1.1.0
Python 1.9.0
.NET 1.10.0
JS 3.0.0 | 0.11.0 | Unsupported | | | June 22nd 2023 | 1.10.8
| 1.10.0 | Java 1.8.0
Go 1.7.0
PHP 1.1.0
Python 1.9.0
.NET 1.10.0
JS 3.0.0 | 0.11.0 | Unsupported | | @@ -138,10 +138,9 @@ General guidance on upgrading can be found for [self hosted mode]({{< ref self-h | 1.10.0 | N/A | 1.10.8 | | 1.11.0 | N/A | 1.11.4 | | 1.12.0 | N/A | 1.12.4 | -| 1.13.0 | N/A | 1.13.2 | -| 1.13.0 | N/A | 1.13.3 | +| 1.12.0 to 1.13.0 | N/A | 1.13.4 | | 1.13.0 | N/A | 1.13.4 | -| 1.14.0 | N/A | 1.14.0 | +| 1.13.0 to 1.14.0 | N/A | 1.14.0 | ## Upgrade on Hosting platforms From 8603cdaa2d51414961896eadd4453b06dda1daff Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 14 Aug 2024 12:30:51 -0400 Subject: [PATCH 91/92] remove website root Signed-off-by: Hannah Hunter --- .github/workflows/website-root.yml | 109 ----------------------------- 1 file changed, 109 deletions(-) delete mode 100644 .github/workflows/website-root.yml diff --git a/.github/workflows/website-root.yml b/.github/workflows/website-root.yml deleted file mode 100644 index aa2b3fe0a..000000000 --- a/.github/workflows/website-root.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: Azure Static Web App Root - -on: - workflow_dispatch: - push: - branches: - - v1.14 - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - v1.14 - -concurrency: - # Cancel the previously triggered build for only PR build. - group: website-${{ github.event.pull_request.number || github.sha }} - cancel-in-progress: true - -jobs: - build_and_deploy_job: - name: Build Hugo Website - if: github.event.action != 'closed' - runs-on: ubuntu-latest - env: - SWA_BASE: 'proud-bay-0e9e0e81e' - HUGO_ENV: production - steps: - - name: Checkout docs repo - uses: actions/checkout@v3 - with: - submodules: true - - name: Setup Node - uses: actions/setup-node@v2 - with: - node-version: '14' - - name: Setup Hugo - uses: peaceiris/actions-hugo@v2.5.0 - with: - hugo-version: 0.102.3 - extended: true - - name: Setup Docsy - run: | - cd daprdocs - git submodule update --init --recursive - sudo npm install -D --save autoprefixer - sudo npm install -D --save postcss-cli - - name: Build Hugo Website - run: | - cd daprdocs - git config --global --add safe.directory /github/workspace - if [ $GITHUB_EVENT_NAME == 'pull_request' ]; then - STAGING_URL="https://${SWA_BASE}-${{github.event.number}}.westus2.azurestaticapps.net/" - fi - hugo ${STAGING_URL+-b "$STAGING_URL"} - - name: Deploy docs site - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_PROUD_BAY_0E9E0E81E }} - repo_token: ${{ secrets.GITHUB_TOKEN }} - action: "upload" - app_location: "daprdocs/public/" - api_location: "daprdocs/public/" - output_location: "" - skip_app_build: true - skip_deploy_on_missing_secrets: true - - name: Upload Hugo artifacts - uses: actions/upload-artifact@v3 - with: - name: hugo_build - path: ./daprdocs/public/ - if-no-files-found: error - - close_staging_site: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close Pull Request Job - steps: - - name: Close Pull Request - id: closepullrequest - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_PROUD_BAY_0E9E0E81E }} - action: "close" - skip_deploy_on_missing_secrets: true - - algolia_index: - name: Index site for Algolia - if: github.event_name == 'push' - needs: ['build_and_deploy_job'] - runs-on: ubuntu-latest - env: - ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} - ALGOLIA_API_WRITE_KEY: ${{ secrets.ALGOLIA_API_WRITE_KEY }} - ALGOLIA_INDEX_NAME: daprdocs - steps: - - name: Checkout docs repo - uses: actions/checkout@v2 - with: - submodules: false - - name: Download Hugo artifacts - uses: actions/download-artifact@v3 - with: - name: hugo_build - path: site/ - - name: Install Python packages - run: | - pip install --upgrade bs4 - pip install --upgrade 'algoliasearch>=2.0,<3.0' - - name: Index site - run: python ./.github/scripts/algolia.py ./site \ No newline at end of file From 0752d16c19394c03d009cdbea048a21e6dfcf410 Mon Sep 17 00:00:00 2001 From: Hannah Hunter Date: Wed, 14 Aug 2024 13:46:16 -0400 Subject: [PATCH 92/92] typo Signed-off-by: Hannah Hunter --- .github/workflows/website-v1-15.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/website-v1-15.yml b/.github/workflows/website-v1-15.yml index 1495c5aa1..645aee743 100644 --- a/.github/workflows/website-v1-15.yml +++ b/.github/workflows/website-v1-15.yml @@ -1,4 +1,4 @@ -name: Azure Static Web App v1.14 +name: Azure Static Web App v1.15 on: workflow_dispatch: