Skip to content

Fix Helm chart pre-mounted Terraform binary path mismatch#11880

Open
willdavsmith wants to merge 6 commits into
radius-project:mainfrom
willdavsmith:terraform-path-fix
Open

Fix Helm chart pre-mounted Terraform binary path mismatch#11880
willdavsmith wants to merge 6 commits into
radius-project:mainfrom
willdavsmith:terraform-path-fix

Conversation

@willdavsmith
Copy link
Copy Markdown
Contributor

@willdavsmith willdavsmith commented May 13, 2026

Summary

The terraform-init init container in the dynamic-rp and rp deployments writes the pre-downloaded Terraform binary to a path that the runtime doesn't look at, so the containers silently re-download Terraform from releases.hashicorp.com on every cold start.

Root cause

Two paths must agree, and they don't:

Producerdeploy/Chart/templates/{dynamic-rp,rp}/deployment.yaml:

  • Binary: {{ .Values.<svc>.terraform.path }}/terraform/terraform/terraform
  • Marker: {{ .Values.<svc>.terraform.path }}/.terraform-source

Consumerpkg/recipes/terraform/install.go:

defaultGlobalTerraformBinary = "/terraform/.terraform-global/terraform"
defaultGlobalMarkerFile      = "/terraform/.terraform-global/.terraform-ready"

ensureGlobalTerraformBinary only checks defaultGlobalMarkerFile. When absent it falls through to downloadAndInstallTerraform, which calls releases.hashicorp.com. The pre-mounted binary at /terraform/terraform is never consulted.

Fix

Change the chart, not the Go code. The Go constants are also referenced by tests and non-K8s install paths — the chart is the side that diverged.

The init container scripts now write the binary and marker to the paths expected by install.go:

  • $GLOBAL_DIR/terraform
  • $GLOBAL_DIR/.terraform-ready

A copy at the legacy {terraform.path}/terraform path and the .terraform-source marker are retained for any out-of-tree tooling that may reference them.

A comment is added pointing at the Go constants so future drift is caught at review time.

Verification

# Render and confirm the new path appears in both deployments:
helm template deploy/Chart | grep '/terraform/.terraform-global/.terraform-ready'

# Helm unit tests still pass:
cd deploy/Chart && helm unittest .
# → Tests: 70 passed, 70 total

End-to-end check after install (should report whatever version was set via global.terraform.downloadUrl, not the version compiled into pkg/recipes/terraform/version.go):

POD=$(kubectl get pods -n radius-system -l app.kubernetes.io/name=dynamic-rp -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n radius-system "$POD" -c dynamic-rp -- /terraform/.terraform-global/terraform version

Scope

  • 3 files changed (2 chart templates + 1 new helm-unittest suite), 0 lines of Go.
  • No behavior change for non-K8s callers of install.go.
  • Download fallback in downloadAndInstallTerraform is preserved.

The terraform-init init container in dynamic-rp and rp deployments was
writing the pre-downloaded Terraform binary to {terraform.path}/terraform
with a marker at {terraform.path}/.terraform-source. However,
ensureGlobalTerraformBinary in pkg/recipes/terraform/install.go looks for
the binary at /terraform/.terraform-global/terraform with a marker at
/terraform/.terraform-global/.terraform-ready.

Because the paths did not match, the runtime always fell through to
downloadAndInstallTerraform and downloaded Terraform from
releases.hashicorp.com on every cold start, silently wasting the work
done by the init container.

Update both init container scripts to write the binary and marker file
to the paths expected by install.go. The legacy path is kept as a copy
for any out-of-tree tooling that may still reference it.
Copilot AI review requested due to automatic review settings May 13, 2026 21:52
@willdavsmith willdavsmith requested review from a team as code owners May 13, 2026 21:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 Helm chart init container scripts for rp and dynamic-rp to write the pre-downloaded Terraform binary and readiness marker into the global path expected by the Terraform installer logic (pkg/recipes/terraform/install.go), preventing unnecessary re-downloads on cold start.

Changes:

  • Write Terraform to <terraform.path>/.terraform-global/terraform and create <terraform.path>/.terraform-global/.terraform-ready.
  • Retain the legacy copy at <terraform.path>/terraform and legacy marker <terraform.path>/.terraform-source.
  • Add an in-script comment pointing maintainers to the Go constants to reduce future drift.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
deploy/Chart/templates/rp/deployment.yaml Update terraform init container to populate the global Terraform binary/marker paths (and keep legacy copies).
deploy/Chart/templates/dynamic-rp/deployment.yaml Same global-path update for dynamic-rp terraform init container (and keep legacy copies).

Comment thread deploy/Chart/templates/rp/deployment.yaml Outdated
Comment thread deploy/Chart/templates/rp/deployment.yaml Outdated
Comment thread deploy/Chart/templates/dynamic-rp/deployment.yaml Outdated
Comment thread deploy/Chart/templates/dynamic-rp/deployment.yaml Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.71%. Comparing base (092d8c2) to head (9528e5b).
⚠️ Report is 20 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #11880      +/-   ##
==========================================
+ Coverage   51.41%   51.71%   +0.29%     
==========================================
  Files         716      726      +10     
  Lines       45106    45608     +502     
==========================================
+ Hits        23191    23585     +394     
- Misses      19719    19796      +77     
- Partials     2196     2227      +31     

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

Comment thread deploy/Chart/templates/rp/deployment.yaml
Comment thread deploy/Chart/templates/rp/deployment.yaml
Comment thread deploy/Chart/templates/dynamic-rp/deployment.yaml
Comment thread deploy/Chart/templates/dynamic-rp/deployment.yaml
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread deploy/Chart/templates/rp/deployment.yaml
Comment thread deploy/Chart/templates/dynamic-rp/deployment.yaml
…tests

Copilot review flagged that even with the global path hard-coded in the
init script, the volume mount and pre-mount directory creation still use
.Values.<svc>.terraform.path. If a user overrides that value, the shared
emptyDir is no longer mounted at /terraform and the runtime (which
hard-codes /terraform/.terraform-global in pkg/recipes/terraform/install.go)
will not see the pre-downloaded binary, silently reintroducing the
download-on-cold-start bug.

Fix:
- Add a Helm 'fail' guard in both rp/deployment.yaml and
  dynamic-rp/deployment.yaml that errors at template-render time when
  <svc>.terraform.path != "/terraform" and terraform pre-download is
  enabled. This makes the unsupported override loud instead of silent.
- Add a new helm-unittest suite (tests/terraform_init_test.yaml) that
  asserts the rendered init container script writes to
  /terraform/.terraform-global/{terraform,.terraform-ready}, verifies
  the fail guard fires for both deployments, and verifies the guard is
  bypassed when global.terraform.enabled=false.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread deploy/Chart/tests/terraform_init_test.yaml Outdated
Comment thread deploy/Chart/tests/terraform_init_test.yaml Outdated
Match the convention used in deploy/Chart/tests/helpers_test.yaml so
each assertion explicitly states which rendered template it validates,
rather than relying on the test-level 'template:' key. The suite-level
'templates:' list is kept so helm-unittest knows which templates are
in scope for the suite.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comment thread deploy/Chart/templates/rp/deployment.yaml Outdated
Comment thread deploy/Chart/templates/dynamic-rp/deployment.yaml Outdated
Comment thread deploy/Chart/tests/terraform_init_test.yaml Outdated
@willdavsmith willdavsmith marked this pull request as draft May 18, 2026 16:34
The previous fail guard only fired when global.terraform.enabled=true,
but the runtime hard-codes /terraform regardless: the rp and dynamic-rp
configmaps both set terraform.path: "/terraform", and
pkg/recipes/terraform/install.go uses /terraform/.terraform-global.
Allowing the override when pre-download is disabled produced an
inconsistent deployment (volume mounted elsewhere while the runtime
still looked in /terraform).

Move the fail guard out of the global.terraform.enabled block to the
top of both deployment templates so it runs on every render. Update
the test suite to assert failure even when pre-download is disabled,
and update the error message to reference the configmap as well so
the root cause is discoverable from the failure.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread deploy/Chart/tests/terraform_init_test.yaml
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

@willdavsmith willdavsmith marked this pull request as ready for review May 18, 2026 18:47
@DariuszPorowski DariuszPorowski requested a review from sylvainsf May 18, 2026 18:48
After PR radius-project#11880 fixed the binary-path mismatch, the dynamic-rp and rp
containers actually consume the pre-mounted Terraform binary written
by the chart's terraform-init init container. However, the init script
was still fetching 'latest' from the HashiCorp API while the runtime
download fallback (pkg/recipes/terraform/install.go) pins to
terraformVersion (1.14.9) in version.go.

This silent version mismatch broke Test_TerraformRecipe_Context and
Test_TerraformRecipe_KubernetesRedis in functional tests on every
commit of this PR: 'latest' Terraform handled the recipe modules'
'context' variable injection differently than 1.14.9.

Fix:
- Add global.terraform.version (default '1.14.9') to deploy/Chart/values.yaml.
- Update both terraform-init init container scripts to build the
  download URL from $TERRAFORM_VERSION instead of querying the
  HashiCorp 'latest' API. global.terraform.downloadUrl still overrides.
- Add helm-unittest assertions that the rendered script contains
  TERRAFORM_VERSION="1.14.9".
- Add TestTerraformVersionMatchesHelmChart Go test that asserts the
  chart default matches terraformVersion in version.go, so future
  bumps of either side fail fast in CI instead of in functional tests.
@radius-functional-tests
Copy link
Copy Markdown

radius-functional-tests Bot commented May 18, 2026

Radius functional test overview

🔍 Go to test action run

Click here to see the test run details
Name Value
Repository willdavsmith/radius
Commit ref a3df09a
Unique ID func8c9305c3d0
Image tag pr-func8c9305c3d0
  • gotestsum 1.13.0
  • KinD: v0.29.0
  • 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-func8c9305c3d0
  • 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-func8c9305c3d0
  • dynamic-rp test image location: ghcr.io/radius-project/dev/dynamic-rp:pr-func8c9305c3d0
  • controller test image location: ghcr.io/radius-project/dev/controller:pr-func8c9305c3d0
  • ucp test image location: ghcr.io/radius-project/dev/ucpd:pr-func8c9305c3d0
  • 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...
⌛ Starting ucp-cloud functional tests...
✅ ucp-cloud functional tests succeeded
✅ corerp-cloud functional tests succeeded

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants