Add deploy workflow - #12250
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Files
|
Unit Tests 2 files ± 0 452 suites +2 7m 27s ⏱️ -7s Results for commit 8ac11a8. ± Comparison against base commit 62b741f. This pull request removes 33 and adds 121 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12250 +/- ##
==========================================
+ Coverage 52.87% 53.03% +0.16%
==========================================
Files 751 755 +4
Lines 48353 48793 +440
==========================================
+ Hits 25566 25879 +313
- Misses 20387 20479 +92
- Partials 2400 2435 +35 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Signed-off-by: sk593 <shruthikumar@microsoft.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
Signed-off-by: Shruthi Kumar <shruthikumar2401@gmail.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
7edf7a1 to
1974957
Compare
Review: PR #12250 — Add deploy workflowReviewer: @sylvainsf · Against: Repo Radius feature spec (#12078, Investments 3 & 4) and issue #12118 SummaryStrong real-world deploy workflow — the recipe-pack/environment model, the What’s good (keep as-is)
Blocking1. Hardcoded personal recipe ref
Pinned to a personal in-flight branch — it will break once that branch Spec gaps (Investment 3 contract)2. Dispatch contract:
|
…12264) # Description Adds onto #12250 the two items the Repo Radius feature spec requires for Investment 3 that aren't in the deploy workflow yet: the **`radius_commands` dispatch contract** and a **technical design note**. Targets the `add-deploy-workflow` branch so it folds into #12250. ## What this adds ### `radius_commands` dispatch input (additive — `image` unchanged) The feature spec (Step 3, Investment 3) defines the workflow's dispatch contract as `environment` + `radius_commands`: a single `rad` command string, or a JSON array of command strings run in order (the `rad` prefix omitted), so a frontend can drive arbitrary `rad` commands — not only a single deploy. This adds `radius_commands` as an **optional** input alongside the existing `image`: - When `radius_commands` is set, the workflow runs each command in order, stops on the first failure, and uploads each command's output as the `radius-output` artifact for incremental frontend polling (spec Step 5). - When it's empty, the workflow runs its existing default `rad deploy` of `.radius/app.bicep` with the `image`/`password` parameters — **current behavior is unchanged**. This keeps the convenient single-deploy path while making the documented multi-command contract available to frontends (Copilot app, CLI, etc.). ### Technical design note `eng/design-notes/environments/2026-06-repo-radius-deploy-workflow.md` — the design doc Nicole requires for this feature. It documents Investments 3 & 4: the dispatch contract, the `RADIUS_TARGET_KUBECONFIG` seam, the UCP-credential + projected-OIDC- token model (AWS IRSA / Azure WI), state persistence, and the recipe-pack / image-build flow as implemented in #12250. ## Notes - Co-authored design note (sk593 + sylvainsf) reflecting the #12250 implementation, not the earlier prototype. - The `radius-output` artifact uses the same SHA-pinned `upload-artifact` already in the workflow. ## Type of change - This pull request adds or changes features of Radius and has an approved issue (issue link required). Related: #12118 Builds on: #12250 ## Contributor checklist - An overview of proposed schema changes is included in a linked GitHub issue. - [ ] Yes - [x] Not applicable - A design document is added or updated under `eng/design-notes/` in this repository, if new APIs are being introduced. - [x] Yes - [ ] Not applicable - The design document has been reviewed and approved by Radius maintainers/approvers. - [ ] Yes - [x] Not applicable - A PR for [resource-types-contrib](https://github.com/radius-project/resource-types-contrib/) is created, if resource types or recipes are affected by the changes in this PR. - [ ] Yes - [x] Not applicable - A PR for [dashboard](https://github.com/radius-project/dashboard/) is created, if the Radius Dashboard is affected by the changes in this PR. - [ ] Yes - [x] Not applicable - A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] Yes - [x] Not applicable --------- Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
| run: | | ||
| git clone --depth 1 --branch "$RESOURCE_TYPES_CONTRIB_REF" "$RESOURCE_TYPES_CONTRIB_REPO" /tmp/resource-types-contrib | ||
|
|
||
| for TYPE_YAML in \ | ||
| Compute/containerImages/containerImages.yaml; do | ||
| if [ -f "/tmp/resource-types-contrib/$TYPE_YAML" ]; then | ||
| echo "Registering $TYPE_YAML..." | ||
| rad resource-type create -f "/tmp/resource-types-contrib/$TYPE_YAML" || \ | ||
| (echo "Retrying after 5s..." && sleep 5 && rad resource-type create -f "/tmp/resource-types-contrib/$TYPE_YAML") | ||
| else | ||
| echo "Skipping $TYPE_YAML (not found)" | ||
| fi | ||
| done | ||
| echo "✅ Resource types registered" | ||
|
|
||
| # Build a local Bicep extension from the containerImages type definition. | ||
| CONTAINER_IMAGES_YAML="/tmp/resource-types-contrib/Compute/containerImages/containerImages.yaml" | ||
| if [ -f "$CONTAINER_IMAGES_YAML" ]; then | ||
| echo "Building local Bicep extension for containerImages..." | ||
| rad bicep publish-extension -f "$CONTAINER_IMAGES_YAML" --target ./radiuscompute.tgz --force | ||
| echo "✅ radiuscompute.tgz built" | ||
| fi |
| run: | | ||
| # pipefail so a failed `rad` whose output is piped through `tee` is detected | ||
| # via PIPESTATUS rather than masked by tee's exit code. | ||
| set -o pipefail | ||
| mkdir -p /tmp/radius-output | ||
| RESULT_FILE=/tmp/radius-output/rad-commands-result.json | ||
|
|
||
| # Ensure the radiusCompute extension is imported for Radius.Compute/containerImages. | ||
| # Without it, the Bicep compiler routes Radius.Compute types to Azure ARM instead of UCP. | ||
| if ! grep -q 'extension radiusCompute' "$APP_FILE" 2>/dev/null; then | ||
| echo "Injecting 'extension radiusCompute' into $APP_FILE" | ||
| sed -i '/^extension radius$/a extension radiusCompute' "$APP_FILE" 2>/dev/null || true | ||
| fi | ||
|
|
||
|
|
||
| # Aggregate result accumulators. The result is written by a trap on EXIT so | ||
| # the combined `rad-commands-result` artifact is complete even when a command | ||
| # fails and the step exits early. | ||
| COMMANDS_JSON='[]' | ||
| OVERALL_OUTCOME="succeeded" | ||
| OVERALL_EXIT=0 | ||
| REQUESTED=0 | ||
| RAN=0 | ||
|
|
||
| write_result() { | ||
| jq -n \ | ||
| --arg outcome "$OVERALL_OUTCOME" \ | ||
| --argjson exitCode "$OVERALL_EXIT" \ | ||
| --arg environment "$ENVIRONMENT" \ | ||
| --argjson requested "$REQUESTED" \ | ||
| --argjson ran "$RAN" \ | ||
| --argjson commands "$COMMANDS_JSON" \ | ||
| '{schemaVersion:"1.0", outcome:$outcome, exitCode:$exitCode, environment:$environment, commandsRequested:$requested, commandsRan:$ran, commands:$commands}' \ | ||
| > "$RESULT_FILE" | ||
| } | ||
| trap write_result EXIT | ||
|
|
||
| # Allowed-command set. Each command's leading verb must be in this list, so | ||
| # commands that do not fit the ephemeral, per-run model — managing or | ||
| # upgrading a control plane, switching workspaces, or changing install state — | ||
| # cannot run. Validation happens before any command runs, so a disallowed | ||
| # command fails fast (exit 2) without deploying anything. | ||
| ALLOWED_VERBS="deploy app resource env recipe group credential resource-type version bicep" | ||
| is_allowed() { | ||
| local verb="${1%% *}" | ||
| case " $ALLOWED_VERBS " in *" $verb "*) return 0 ;; *) return 1 ;; esac | ||
| } | ||
|
|
||
| # Record one command's result into the combined artifact. Keeps live console | ||
| # output via tee while capturing it for the artifact. | ||
| record() { | ||
| local index="$1"; shift | ||
| local display="$1"; shift | ||
| local outfile code | ||
| outfile=$(mktemp) | ||
| echo "::group::rad $display" | ||
| "$@" 2>&1 | tee "$outfile" | ||
| code=${PIPESTATUS[0]} | ||
| echo "::endgroup::" | ||
| RAN=$((RAN + 1)) | ||
| COMMANDS_JSON=$(jq \ | ||
| --argjson index "$index" \ | ||
| --arg cmd "$display" \ | ||
| --argjson code "$code" \ | ||
| --arg outcome "$([ "$code" -eq 0 ] && echo succeeded || echo failed)" \ | ||
| --rawfile out "$outfile" \ | ||
| '. + [{index:$index, command:$cmd, outcome:$outcome, exitCode:$code, output:$out}]' \ | ||
| <<<"$COMMANDS_JSON") | ||
| rm -f "$outfile" | ||
| return "$code" | ||
| } | ||
|
|
||
| if [ -n "${RAD_COMMANDS//[[:space:]]/}" ]; then | ||
| # Caller-supplied commands: a single command string or a JSON array, run in | ||
| # order with the `rad` prefix omitted. | ||
| if printf '%s' "$RAD_COMMANDS" | jq -e 'type == "array"' >/dev/null 2>&1; then | ||
| mapfile -t COMMANDS < <(printf '%s' "$RAD_COMMANDS" | jq -r '.[]') | ||
| else | ||
| COMMANDS=("$RAD_COMMANDS") | ||
| fi | ||
|
|
||
| # Drop blank entries and validate every command before running any of them. | ||
| CLEAN=() | ||
| for cmd in "${COMMANDS[@]}"; do | ||
| [ -z "${cmd//[[:space:]]/}" ] && continue | ||
| CLEAN+=("$cmd") | ||
| done | ||
| REQUESTED=${#CLEAN[@]} | ||
|
|
||
| for cmd in "${CLEAN[@]}"; do | ||
| if ! is_allowed "$cmd"; then | ||
| OVERALL_OUTCOME="disallowed_command" | ||
| OVERALL_EXIT=2 | ||
| COMMANDS_JSON=$(jq --arg cmd "$cmd" \ | ||
| '. + [{command:$cmd, outcome:"disallowed", exitCode:2, errorMessage:"Command leading verb is not in the allowed-command set."}]' \ | ||
| <<<"$COMMANDS_JSON") | ||
| echo "Disallowed command: rad $cmd" >&2 | ||
| exit 2 | ||
| fi | ||
| done | ||
|
|
||
| idx=0 | ||
| for cmd in "${CLEAN[@]}"; do | ||
| verb="${cmd%% *}" | ||
| if [ "$verb" = "deploy" ]; then | ||
| # For deploy commands, append the image and the secret application | ||
| # parameters. Build an argv array so secret values with special | ||
| # characters are never word-split, and keep them out of the | ||
| # recorded command string (only the non-secret `$cmd` is recorded). | ||
| # shellcheck disable=SC2086 | ||
| read -ra CMD_ARGV <<< "$cmd" | ||
| EXTRA_PARAMS=() | ||
| if [ -n "$APP_IMAGE" ]; then | ||
| EXTRA_PARAMS+=(--parameters "image=$APP_IMAGE") | ||
| fi | ||
| if [ -n "${RADIUS_DEPLOY_PARAMS//[[:space:]]/}" ]; then | ||
| while IFS= read -r _pname; do | ||
| [ -z "$_pname" ] && continue | ||
| _pval=$(printf '%s' "$RADIUS_DEPLOY_PARAMS" | jq -r --arg k "$_pname" '.[$k]') | ||
| EXTRA_PARAMS+=(--parameters "$_pname=$_pval") | ||
| done < <(printf '%s' "$RADIUS_DEPLOY_PARAMS" | jq -r 'keys_unsorted[]') | ||
| fi | ||
| if ! record "$idx" "$cmd" rad "${CMD_ARGV[@]}" "${EXTRA_PARAMS[@]}"; then | ||
| OVERALL_OUTCOME="command_failed" | ||
| OVERALL_EXIT=1 | ||
| echo "Command failed: rad $cmd" >&2 | ||
| exit 1 | ||
| fi | ||
| else | ||
| # Intentional word-split: the command string carries its own args. | ||
| # shellcheck disable=SC2086 | ||
| if ! record "$idx" "$cmd" rad $cmd; then | ||
| OVERALL_OUTCOME="command_failed" | ||
| OVERALL_EXIT=1 | ||
| echo "Command failed: rad $cmd" >&2 | ||
| exit 1 | ||
| fi | ||
| fi | ||
| idx=$((idx + 1)) | ||
| done | ||
| echo "✅ Radius commands complete." | ||
| else | ||
| # Default behavior: deploy the app bicep. Build the parameters as an array | ||
| # so values with special characters are not mangled by the shell. | ||
| REQUESTED=1 | ||
| DEPLOY_PARAMS=() | ||
| if [ -n "$APP_IMAGE" ]; then | ||
| DEPLOY_PARAMS+=(--parameters "image=$APP_IMAGE") | ||
| fi | ||
| # Expand the application parameters JSON ({"name":"value", …}) into | ||
| # --parameters name=value pairs. Each value is read back from the JSON | ||
| # by key with jq so embedded '=', spaces, or newlines are preserved and | ||
| # never re-split by the shell. | ||
| if [ -n "${RADIUS_DEPLOY_PARAMS//[[:space:]]/}" ]; then | ||
| while IFS= read -r _pname; do | ||
| [ -z "$_pname" ] && continue | ||
| _pval=$(printf '%s' "$RADIUS_DEPLOY_PARAMS" | jq -r --arg k "$_pname" '.[$k]') | ||
| DEPLOY_PARAMS+=(--parameters "$_pname=$_pval") | ||
| done < <(printf '%s' "$RADIUS_DEPLOY_PARAMS" | jq -r 'keys_unsorted[]') | ||
| fi | ||
| # The recorded command string omits the parameters so secret values are | ||
| # not written into the result artifact. | ||
| if ! record 0 "deploy $APP_FILE --environment $ENVIRONMENT" \ | ||
| rad deploy "$APP_FILE" --environment "$ENVIRONMENT" "${DEPLOY_PARAMS[@]}"; then | ||
| OVERALL_OUTCOME="command_failed" | ||
| OVERALL_EXIT=1 | ||
| echo "Deployment failed" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "✅ Deployment complete." | ||
| fi |
| run: | | ||
| k3d cluster create radius-cp \ | ||
| --volume /var/run/docker.sock:/var/run/docker.sock \ | ||
| --volume "${{ github.workspace }}/:/app/demo" \ |
…12343) ## Summary `Radius.Compute/containerImages` now ships with the published Radius Bicep extension and its type is registered on the control plane by Radius itself. The deploy workflow therefore no longer needs to register the type or wire up a local Bicep extension at deploy time. ## Changes - **Delete the `register-resource-types` composite action.** Its only job was to clone `resource-types-contrib`, `rad resource-type create` the `containerImages` type, and build a local `radiuscompute.tgz` Bicep extension — all now redundant. - **Remove the "Register resource types" step** from both provider workflows (`run-rad-commands-aws.yml`, `run-rad-commands-azure.yml`). - **`run-rad-commands` action:** drop the `extension radiusCompute` injection into the app bicep and the `radiusCompute` merge/create into the repo's `bicepconfig.json`. The app's `extension radius` import and the user's own bicepconfig now cover `Radius.Compute/containerImages`. - **README** updated to match. The containerImages Terraform recipe (in-cluster image build/push) is unchanged — it is still defined in the recipe pack. --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…12345) ## Summary The `.github/extension/run-rad-commands-{aws,azure}.yml` reference copies in this repo had drifted from the ai-extensions templates that are actually generated into user repos at deploy time. This PR resyncs them so the reviewed reference and the runtime source of truth match. ## Changes - **Deploy `radius-env.bicep` from the app directory (both providers).** The recipe-pack/environment step wrote `radius-env.bicep` to the workspace root and ran `rad deploy` there. bicep resolves `bicepconfig.json` nearest the `.bicep` file, so when the app and its `bicepconfig.json` live under `.radius/`, the root deploy could not resolve the `radius` extension (`extension radius`) and the environment/recipe-pack deploy failed. Now it writes and deploys from `dirname "$APP_FILE"` (e.g. `.radius/`), picking up the repo's own bicepconfig. - **Azure: switch to the AVM recipe pack.** Replace the inline generated recipe pack with the Azure Verified Modules pack downloaded from `resource-types-contrib` (`recipepack/azure/aks-recipepack.bicep`), matching the ai-extensions Azure template. ## Context Follows #12343 (which removed containerImages type registration). The AWS copy needed only the app-directory fix; the Azure copy also needed the AVM pack swap. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reflect that the deploy workflow no longer registers resource types (the Radius.Compute/containerImages type ships with the published radius Bicep extension) and that the recipe pack is deployed from the app file's directory so the repo's own bicepconfig.json resolves the radius extension. Azure downloads the azure-avm pack from resource-types-contrib; AWS generates an inline pack. Updates the design note, extension README, and radius-deploy skill: - Drop register-resource-types from composite-action lists and mermaid. - Rewrite the recipe-pack section for AVM download vs inline generation. - Note the app-dir deploy / bicepconfig resolution. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
Radius functional test overviewClick here to see the test run details
Test Status⌛ Building Radius and pushing container images for functional tests... |
…12264) Adds onto #12250 the two items the Repo Radius feature spec requires for Investment 3 that aren't in the deploy workflow yet: the **`radius_commands` dispatch contract** and a **technical design note**. Targets the `add-deploy-workflow` branch so it folds into #12250. The feature spec (Step 3, Investment 3) defines the workflow's dispatch contract as `environment` + `radius_commands`: a single `rad` command string, or a JSON array of command strings run in order (the `rad` prefix omitted), so a frontend can drive arbitrary `rad` commands — not only a single deploy. This adds `radius_commands` as an **optional** input alongside the existing `image`: - When `radius_commands` is set, the workflow runs each command in order, stops on the first failure, and uploads each command's output as the `radius-output` artifact for incremental frontend polling (spec Step 5). - When it's empty, the workflow runs its existing default `rad deploy` of `.radius/app.bicep` with the `image`/`password` parameters — **current behavior is unchanged**. This keeps the convenient single-deploy path while making the documented multi-command contract available to frontends (Copilot app, CLI, etc.). `eng/design-notes/environments/2026-06-repo-radius-deploy-workflow.md` — the design doc Nicole requires for this feature. It documents Investments 3 & 4: the dispatch contract, the `RADIUS_TARGET_KUBECONFIG` seam, the UCP-credential + projected-OIDC- token model (AWS IRSA / Azure WI), state persistence, and the recipe-pack / image-build flow as implemented in #12250. - Co-authored design note (sk593 + sylvainsf) reflecting the #12250 implementation, not the earlier prototype. - The `radius-output` artifact uses the same SHA-pinned `upload-artifact` already in the workflow. - This pull request adds or changes features of Radius and has an approved issue (issue link required). Related: #12118 Builds on: #12250 - An overview of proposed schema changes is included in a linked GitHub issue. - [ ] Yes - [x] Not applicable - A design document is added or updated under `eng/design-notes/` in this repository, if new APIs are being introduced. - [x] Yes - [ ] Not applicable - The design document has been reviewed and approved by Radius maintainers/approvers. - [ ] Yes - [x] Not applicable - A PR for [resource-types-contrib](https://github.com/radius-project/resource-types-contrib/) is created, if resource types or recipes are affected by the changes in this PR. - [ ] Yes - [x] Not applicable - A PR for [dashboard](https://github.com/radius-project/dashboard/) is created, if the Radius Dashboard is affected by the changes in this PR. - [ ] Yes - [x] Not applicable - A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] Yes - [x] Not applicable --------- Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
…12264) Adds onto #12250 the two items the Repo Radius feature spec requires for Investment 3 that aren't in the deploy workflow yet: the **`radius_commands` dispatch contract** and a **technical design note**. Targets the `add-deploy-workflow` branch so it folds into #12250. The feature spec (Step 3, Investment 3) defines the workflow's dispatch contract as `environment` + `radius_commands`: a single `rad` command string, or a JSON array of command strings run in order (the `rad` prefix omitted), so a frontend can drive arbitrary `rad` commands — not only a single deploy. This adds `radius_commands` as an **optional** input alongside the existing `image`: - When `radius_commands` is set, the workflow runs each command in order, stops on the first failure, and uploads each command's output as the `radius-output` artifact for incremental frontend polling (spec Step 5). - When it's empty, the workflow runs its existing default `rad deploy` of `.radius/app.bicep` with the `image`/`password` parameters — **current behavior is unchanged**. This keeps the convenient single-deploy path while making the documented multi-command contract available to frontends (Copilot app, CLI, etc.). `eng/design-notes/environments/2026-06-repo-radius-deploy-workflow.md` — the design doc Nicole requires for this feature. It documents Investments 3 & 4: the dispatch contract, the `RADIUS_TARGET_KUBECONFIG` seam, the UCP-credential + projected-OIDC- token model (AWS IRSA / Azure WI), state persistence, and the recipe-pack / image-build flow as implemented in #12250. - Co-authored design note (sk593 + sylvainsf) reflecting the #12250 implementation, not the earlier prototype. - The `radius-output` artifact uses the same SHA-pinned `upload-artifact` already in the workflow. - This pull request adds or changes features of Radius and has an approved issue (issue link required). Related: #12118 Builds on: #12250 - An overview of proposed schema changes is included in a linked GitHub issue. - [ ] Yes - [x] Not applicable - A design document is added or updated under `eng/design-notes/` in this repository, if new APIs are being introduced. - [x] Yes - [ ] Not applicable - The design document has been reviewed and approved by Radius maintainers/approvers. - [ ] Yes - [x] Not applicable - A PR for [resource-types-contrib](https://github.com/radius-project/resource-types-contrib/) is created, if resource types or recipes are affected by the changes in this PR. - [ ] Yes - [x] Not applicable - A PR for [dashboard](https://github.com/radius-project/dashboard/) is created, if the Radius Dashboard is affected by the changes in this PR. - [ ] Yes - [x] Not applicable - A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ ] Yes - [x] Not applicable --------- Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com> Signed-off-by: sk593 <shruthikumar@microsoft.com>
Description
Adds the Repo Radius deploy workflow — an auto-generated GitHub Actions workflow that deploys a Radius application against a user's existing AKS/EKS cluster, plus its contract documentation and the
radius-deployskill that drives it.The workflow stands up an ephemeral k3d Radius control plane on the runner, points it at the user's external target cluster via the chart's
targetClusterseam, restores persisted state, runsrad deployon.radius/app.bicep, then persists state again and tears the ephemeral cluster down.How the workflow works
azure/loginand/oraws-actions/configure-aws-credentials; no long-lived cloud secrets are stored.RADIUS_TARGET_KUBECONFIG(EKS uses a static bearer-token kubeconfig).radCLI (edge), and Terraform, then installs Radius with--set global.targetCluster.enabled=true,--set database.enabled=true(durable state), and--set dynamicrp.buildkit.enabled=true(in-pod image builds).rad credential register.rad startup) — restores the control-plane PostgreSQL databases and Terraform recipe-state Secrets from theradius-stategit orphan branch (no-op on first run).resource-types-contrib, registersRadius.Compute/containerImages, and creates the environment with a provider-specific Terraform recipe pack.rad deploy .radius/app.bicepwith theimageparameter (defaults to the commit SHA) and the optionalpasswordparameter fromRADIUS_DB_PASSWORD.rad shutdown,if: always()) — backs the databases and Terraform state up to theradius-statebranch even on failure.radius-cpcluster; on failure, logs are uploaded as theradius-logsartifact.Type of change
Fixes: #12118
Contributor checklist
Please verify that the PR meets the following requirements, where applicable:
eng/design-notes/in this repository, if new APIs are being introduced.