Skip to content

Architecture-aware container image builds in deploy workflows - #12640

Open
sylvainsf wants to merge 6 commits into
mainfrom
arch-aware-container-builds
Open

Architecture-aware container image builds in deploy workflows#12640
sylvainsf wants to merge 6 commits into
mainfrom
arch-aware-container-builds

Conversation

@sylvainsf

Copy link
Copy Markdown
Contributor

Description

Radius.Compute/containerImages builds run in an in-cluster BuildKit that is compiled for the runner's architecture (amd64 on standard GitHub-hosted runners). When an app leaves build.platforms unset, the recipe defaults to a multi-arch build (linux/amd64,linux/arm64), so the arm64 half is produced under QEMU emulation, which is roughly an order of magnitude slower and prone to emulation crashes (see the analysis on #12595). This PR lets the Azure and AWS deploy workflows build only the platform(s) the target cluster actually runs, while preserving multi-arch when it is genuinely needed.

This is the upstream half of the contract in radius-project/ai-extensions#300.

Contract

Two template placeholders on the Azure and AWS run-rad-commands workflows, rendered by the extension:

Placeholder Extension default
{{TARGET_CLUSTER_ARCH_MODE}} ${{ vars.RADIUS_BUILD_ARCH_MODE || 'detect' }}
{{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}} ${{ vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64' }}

Behavior

Resolved by compute-build-platforms.sh, invoked by the shared run-rad-commands action after the target kubeconfig is configured and before the app is built/deployed:

Mode Cluster Result
detect single-arch build that one platform (no emulation)
detect mixed / undetermined fallback platform list
explicit list (contains /, e.g. linux/amd64) n/a honored verbatim, no detection
empty / unsubstituted placeholder n/a feature off, recipe default applies (existing behavior)

The computed list is exported as RADIUS_EFFECTIVE_BUILD_PLATFORMS and injected as --parameters platforms=<list> only when the app declares a platforms parameter and it was not already supplied via RADIUS_DEPLOY_PARAMS, flowing through the same conditional path as the existing app-image parameter. Apps that do not declare platforms, and templates that do not render the placeholders, are unaffected.

Mixed-arch is a first-class outcome, not an error. A cluster with both amd64 and arm64 nodes resolves to the fallback multi-arch list so images stay portable; only single-arch clusters drop to a single platform to skip emulation.

Design notes

  • Kept the contract small and explicit. Recognized modes are detect, an explicit platform list, or empty/placeholder (off). An unrecognized keyword fails safe to the fallback list with a warning, documented in the workflow and script comments.
  • Detection uses kubectl get nodes -o jsonpath=...nodeInfo.architecture; a failed probe degrades to empty, which the resolver treats as "undetermined" and maps to the fallback.
  • No Radius runtime/CLI changes; this is entirely in the deploy workflow templates, the shared composite action, and a helper script.

Testing

  • make test-build-platforms — new compute-build-platforms_test.sh: mode/detection/override/fallback matrix (single-arch, mixed-arch, unknown-arch, x86_64/aarch64 aliases, normalization, unrecognized mode), plus assertions that both workflows carry the placeholders and wire the inputs, and that the action declares the inputs and runs the helper.
  • make test-run-rad-commands-action — extended deploy-parameters_test.sh: platforms is injected only when declared, skipped when no effective list was computed, and never overrides a RADIUS_DEPLOY_PARAMS-supplied value.
  • shellcheck clean on all shell; all three modified YAML files parse.

Files

File Change
.github/extension/actions/run-rad-commands/compute-build-platforms.sh New: resolves effective build platforms from mode/fallback/detected arches
.github/extension/actions/run-rad-commands/compute-build-platforms_test.sh New: unit + wiring tests
.github/extension/actions/run-rad-commands/action.yml New build-arch-mode / build-fallback-platforms inputs; detection step exporting RADIUS_EFFECTIVE_BUILD_PLATFORMS
.github/extension/actions/run-rad-commands/deploy-parameters.sh Inject platforms when the app declares it and a list was computed
.github/extension/actions/run-rad-commands/deploy-parameters_test.sh Coverage for the injection
.github/extension/run-rad-commands-azure.yml / -aws.yml Placeholders + pass the two inputs to run-rad-commands
build/test.mk test-build-platforms target, added to test

Related

@sylvainsf
sylvainsf requested a review from a team as a code owner August 10, 2026 23:29
Copilot AI lite review requested due to automatic review settings August 10, 2026 23:29
@sylvainsf
sylvainsf requested a review from a team as a code owner August 10, 2026 23:29
@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the shared run-rad-commands deploy workflows/actions to compute an effective container image build platform list based on the target cluster’s node architectures, so single-arch clusters avoid slow/flaky QEMU-emulated multi-arch builds while mixed/undetermined clusters keep the multi-arch fallback.

Changes:

  • Add compute-build-platforms.sh (and tests) to resolve effective build platforms from mode/fallback/detected arches.
  • Wire new build-arch-mode / build-fallback-platforms inputs through the composite action and Azure/AWS workflow templates.
  • Inject --parameters platforms=... only when the app declares platforms and it’s not already provided via RADIUS_DEPLOY_PARAMS; add Make/test coverage.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
build/test.mk Adds test-build-platforms and includes it in make test.
.github/extension/run-rad-commands-azure.yml Adds template placeholders and passes arch inputs to the shared action.
.github/extension/run-rad-commands-aws.yml Adds template placeholders and passes arch inputs to the shared action.
.github/extension/actions/run-rad-commands/action.yml Declares new inputs and adds a step to detect/compute/export RADIUS_EFFECTIVE_BUILD_PLATFORMS.
.github/extension/actions/run-rad-commands/compute-build-platforms.sh New platform resolver script (mode/fallback/detect logic).
.github/extension/actions/run-rad-commands/compute-build-platforms_test.sh New unit + wiring tests for resolver/workflows/action.
.github/extension/actions/run-rad-commands/deploy-parameters.sh Injects platforms parameter when applicable.
.github/extension/actions/run-rad-commands/deploy-parameters_test.sh Adds coverage for platforms injection behavior.
Suppressed comments (2)

.github/extension/actions/run-rad-commands/action.yml:113

  • This currently suppresses kubectl errors and can hide useful diagnostics (RBAC, connectivity, etc.). Prefer letting stderr through while still degrading to empty ARCHES via || true. Also avoid echo "$ARCHES" in command substitution (it can mangle backslashes); use printf instead.
            ARCHES=$(kubectl --kubeconfig "$TARGET_KUBECONFIG" get nodes \
              -o jsonpath='{range .items[*]}{.status.nodeInfo.architecture}{"\n"}{end}' \
              2>/dev/null || true)
            echo "Detected target cluster node architectures: $(echo "$ARCHES" | tr '\n' ' ')"

.github/extension/actions/run-rad-commands/compute-build-platforms.sh:66

  • sort is locale-dependent; without pinning the locale, the "sorted (deterministic) order" guarantee can vary across runner images/locales. Set LC_ALL=C for stable collation.
        sort -u |

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/extension/actions/run-rad-commands/action.yml Outdated
Comment thread .github/extension/actions/run-rad-commands/compute-build-platforms.sh Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Unit Tests

    2 files  ±0    457 suites  ±0   6m 57s ⏱️ -31s
6 225 tests ±0  6 223 ✅ ±0  2 💤 ±0  0 ❌ ±0 
7 457 runs  ±0  7 455 ✅ ±0  2 💤 ±0  0 ❌ ±0 

Results for commit bd55a5b. ± Comparison against base commit 78e12e0.

♻️ This comment has been updated with latest results.

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.18%. Comparing base (78e12e0) to head (bd55a5b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12640      +/-   ##
==========================================
- Coverage   54.19%   54.18%   -0.02%     
==========================================
  Files         770      770              
  Lines       51085    51085              
==========================================
- Hits        27688    27682       -6     
- Misses      20790    20793       +3     
- Partials     2607     2610       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Functional Tests - corerp-cloud

30 tests   29 ✅  21m 10s ⏱️
 2 suites   1 💤
 1 files     0 ❌

Results for commit 4ff4d66.

♻️ This comment has been updated with latest results.

@sylvainsf

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Addressed in bd0005c.

  • set -u on the kubeconfig assignment (action.yml L106/L110): Fixed. TARGET_KUBECONFIG now default-expands ${RADIUS_TARGET_KUBECONFIG:-}, so an unset value no longer trips set -u before the empty case is handled; detect mode degrades cleanly to the fallback when no kubeconfig is present.
  • Malformed MODE contract header comment: Fixed. Rewrote the block so the bullets and parenthesis are well-formed.
  • stderr capture in the test (L71): This one is a false positive, so I left it. The idiom "$(cmd 2>&1 >/dev/null)" is the correct order for "capture stderr, discard stdout": 2>&1 first points fd2 at the command-substitution capture (currently fd1), then >/dev/null redirects fd1 only. Reversing to >/dev/null 2>&1 would discard both. The test asserts the warning is captured and it passes, which confirms stderr is captured.

The corerp-cloud functional failure is unrelated to this change (this PR only touches the extension workflow YAML, the shared composite action, and a helper script; that lane does not exercise run-rad-commands). Rebasing on main will re-trigger it.

Radius.Compute/containerImages builds run in an in-cluster BuildKit compiled
for the runner's architecture (amd64 on standard GitHub-hosted runners). When
an app leaves build.platforms unset the recipe defaults to multi-arch
(linux/amd64,linux/arm64), so the arm64 half builds under QEMU emulation --
much slower and prone to emulation crashes. This lets the deploy workflows
build only the platform(s) the target cluster actually runs.

Contract (consumed by ai-extensions, radius-project/ai-extensions#300) via two
template placeholders on the Azure and AWS run-rad-commands workflows:
  {{TARGET_CLUSTER_ARCH_MODE}}                -> vars.RADIUS_BUILD_ARCH_MODE || 'detect'
  {{TARGET_CLUSTER_ARCH_FALLBACK_PLATFORMS}}  -> vars.RADIUS_BUILD_PLATFORMS || 'linux/amd64,linux/arm64'

Behavior (compute-build-platforms.sh, invoked by the shared run-rad-commands
action after the target kubeconfig is configured and before the deploy):
  - mode 'detect', single-arch cluster  -> build that one platform (no emulation)
  - mode 'detect', mixed or undetermined -> fallback platform list
  - explicit platform list (contains '/') -> honored verbatim, no detection
  - empty / unsubstituted placeholder     -> feature off, recipe default applies

The computed list is exported as RADIUS_EFFECTIVE_BUILD_PLATFORMS and injected
as `--parameters platforms=<list>` only when the app declares a `platforms`
parameter and it was not already supplied via RADIUS_DEPLOY_PARAMS, flowing
through the same conditional path as the existing app-image parameter. Apps
that do not opt in are unaffected.

Tests:
  - compute-build-platforms_test.sh: mode/detection/override/fallback matrix
    plus workflow and action wiring assertions (make test-build-platforms).
  - deploy-parameters_test.sh: platforms injection is gated on declaration,
    an empty computed list, and RADIUS_DEPLOY_PARAMS precedence.
Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
…comment

- action.yml: default-expand RADIUS_TARGET_KUBECONFIG so an unset value does
  not trip set -u before the empty case is handled (degrades to fallback).
- compute-build-platforms.sh: rewrite the malformed MODE contract header
  comment (unfinished parenthesis / broken bullet) for readability.

Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
@sylvainsf
sylvainsf force-pushed the arch-aware-container-builds branch from bd0005c to 84c8ca4 Compare August 11, 2026 00:18
Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
Signed-off-by: Sylvain Niles <sylvainniles@microsoft.com>
@radius-functional-tests

radius-functional-tests Bot commented Aug 14, 2026

Copy link
Copy Markdown

Radius functional test overview

🔍 Go to test action run

Click here to see the test run details
Name Value
Repository radius-project/radius
Commit ref bd55a5b
Unique ID funcbb0f8e1896
Image tag pr-funcbb0f8e1896
  • Dapr: 1.14.4
  • Azure KeyVault CSI driver: 1.4.2
  • Azure Workload identity webhook: 1.3.0
  • Bicep recipe location ghcr.io/radius-project/dev/test/testrecipes/test-bicep-recipes/<name>:pr-funcbb0f8e1896
  • Terraform recipe location http://tf-module-server.radius-test-tf-module-server.svc.cluster.local/<name>.zip (in cluster)
  • applications-rp test image location: ghcr.io/radius-project/dev/applications-rp:pr-funcbb0f8e1896
  • dynamic-rp test image location: ghcr.io/radius-project/dev/dynamic-rp:pr-funcbb0f8e1896
  • controller test image location: ghcr.io/radius-project/dev/controller:pr-funcbb0f8e1896
  • ucp test image location: ghcr.io/radius-project/dev/ucpd:pr-funcbb0f8e1896
  • deployment-engine test image location: ghcr.io/radius-project/deployment-engine:latest

Test Status

⌛ Building Radius and pushing container images for functional tests...
✅ Container images build succeeded
⌛ Publishing Bicep Recipes for functional tests...
✅ Recipe publishing succeeded
⌛ Starting corerp-cloud functional tests...
✅ ucp-cloud functional tests succeeded
✅ corerp-cloud functional tests succeeded

@sylvainsf
sylvainsf enabled auto-merge August 14, 2026 18:20
@github-actions

Copy link
Copy Markdown

Functional Tests - dynamicrp-noncloud

67 tests  ±0   65 ✅  - 2   19m 4s ⏱️ + 1m 6s
 1 suites ±0    0 💤 ±0 
 1 files   ±0    2 ❌ +2 

For more details on these failures, see this check.

Results for commit bd55a5b. ± Comparison against base commit 78e12e0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants