Skip to content

feat(cli): install or reuse cert-manager for component identity - #13344

Open
sk593 wants to merge 7 commits into
mainfrom
sk593-a1-cert-manager-install
Open

sk593 wants to merge 7 commits into
mainfrom
sk593-a1-cert-manager-install

Conversation

@sk593

@sk593 sk593 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Layer 1 of Stack A: Component identity (mTLS) for the internal component authorization design (#13086, follow-up #13230). Stacked on #13216 (top of Stack 0, now merged; A1 now targets main).

Summary

When authorization is turned on (global.rbac.enabled=true or global.rbac.dryRun=true, from --set or from values stored on the Radius release), rad install kubernetes and rad upgrade kubernetes check for cert-manager before touching Radius. Based on the design's cluster-state table, they then install it, reuse it, or fail:

Cluster state Behavior
No cert-manager Install jetstack cert-manager v1.21.2 (CRDs enabled) into namespace cert-manager, labeled as rad-owned. rad upgrade upgrades it with Radius. A later rad install reuses it if the release is deployed and its controller is running; otherwise it repairs it with helm upgrade.
cert-manager ≥ v1.20.0 that rad did not install Reuse it. It is never upgraded or removed.
cert-manager < v1.20.0 Stop before installing Radius, with an error that names v1.20.0.
certificates.cert-manager.io CRD present, no controller Stop with a "repair or remove" error. A second cert-manager is not installed.

The mode follows the chart's rules: Go template truthiness for enabled/dryRun (so 1 or "true\n" count as on), and --set global=null or global.rbac=null clears stored values.

With the default mode (off), install and upgrade make no cert-manager detection calls and no extra Helm calls; behavior is unchanged. There are no new flags.

Reason for change

Component mTLS (Stack A) needs cert-manager to issue service identities. This layer only gets cert-manager onto the cluster safely. Nothing uses it yet.

Decisions

  • Versions: default v1.21.2 and minimum 1.20.0 (latest stable and oldest supported minor when selected per https://cert-manager.io/docs/releases/). Both are constants in pkg/cli/helm/cert_manager.go, with a link to the releases page. Detection reads the controller Deployment's app.kubernetes.io/version label and falls back to its image tag.
  • Ownership marker: the Helm v4 release label app.kubernetes.io/managed-by=rad, set at install. It is stored on the release secrets and carried across helm upgrade. Only a cert-manager/cert-manager release with this label is upgraded or uninstalled by rad. helm get metadata does not display this custom label; inspect the release Secrets as shown below.
  • Uninstall: rad uninstall kubernetes already removes Contour, so it can also remove a rad-owned cert-manager. A user-installed cert-manager is never removed.
    • rad installs cert-manager with crds.keep=false, so its CRDs go with the release. Otherwise a later authz install would hit the "CRD without controller" guard.
    • Deleting those CRDs also deletes every cert-manager resource in the cluster. So before removing a rad-owned cert-manager, uninstall lists Certificates, CertificateRequests, and Issuers in all namespaces, plus ClusterIssuers.
    • If any exist outside the Radius namespace, or the check fails, uninstall keeps cert-manager, prints one line (Keeping cert-manager: other workloads use it (N resources, e.g. ns/kind/name)), and still succeeds.
    • The confirmation plan lists cert-manager whenever rad installed it, before this check runs. If the ownership lookup fails, the plan omits it with a warning and uninstall continues.
    • This adds one Helm get to the uninstall path, plus four list calls when the release is rad-owned.
  • Stored values: CheckRadiusInstall now returns the user values already stored on the Radius release (from the existing helm get, so no extra call). Install with --reinstall and upgrade without --reset-values use them to work out the mode.
  • Size: about 474 changed lines of production code (excluding generated mocks), plus tests.

How to test

Unit tests and lint:

go test ./pkg/cli/...
golangci-lint run ./pkg/cli/helm/... ./pkg/cli/cmd/install/... ./pkg/cli/cmd/upgrade/... ./pkg/cli/cmd/uninstall/...   # pinned v2.14.0

Manual (kind on Podman, isolated HOME; the images in the original run were Stack 0 builds loaded with kind load image-archive). With Podman 6, use kind v0.33.0; v0.29.0 fails to list clusters.

export KIND_EXPERIMENTAL_PROVIDER=podman
kind create cluster --name radius-authz-a1
mkdir -p /tmp/a1-rad
go build -o /tmp/a1-rad/rad ./cmd/rad
mkdir -p /tmp/a1-home/.kube && kind get kubeconfig --name radius-authz-a1 > /tmp/a1-home/.kube/config
# Use a function rather than a command string so this works in both bash and zsh.
a1rad() { env HOME=/tmp/a1-home /tmp/a1-rad/rad "$@"; }
a1rad version # verify this is the CLI built from A1, not an older rad on PATH
# image overrides for local images omitted: rp/controller/dynamicrp/ucp .image/.tag, controller.imagePullPolicy=IfNotPresent

a1rad install kubernetes --chart deploy/Chart --set global.rbac.dryRun=true   # installs cert-manager v1.21.2, then Radius
kubectl --context kind-radius-authz-a1 -n cert-manager rollout status deployment/cert-manager --timeout=300s
# Read-only controller rollout wait, not a certificate-issuance check.
kubectl --context kind-radius-authz-a1 -n cert-manager get secrets -l owner=helm,name=cert-manager --show-labels
# Expected: app.kubernetes.io/managed-by=rad. helm get metadata does not show it.
a1rad install kubernetes --chart deploy/Chart --set global.rbac.dryRun=true --reinstall   # upgrades the rad-owned release
a1rad upgrade kubernetes --chart deploy/Chart --skip-preflight                # stored dryRun -> upgrades rad-owned cert-manager
a1rad uninstall kubernetes --yes                                              # plan lists cert-manager; release and CRDs removed
# reinstall with dryRun, kubectl apply an Issuer + Certificate in namespace app, then uninstall -> cert-manager kept
a1rad install kubernetes --chart deploy/Chart                                 # default: no cert-manager
helm --kube-context kind-radius-authz-a1 install cert-manager cert-manager --repo https://charts.jetstack.io --version v1.20.2 -n cert-manager --create-namespace --set crds.enabled=true --wait
a1rad install kubernetes --chart deploy/Chart --set global.rbac.dryRun=true   # "Using existing cert-manager v1.20.2"
# fresh cluster with cert-manager v1.19.4 -> install fails naming v1.20.0, and radius-system is never created
kind delete cluster --name radius-authz-a1

When using hack/authz/up.sh: build the CLI separately and export RAD=/absolute/path/to/the/built/rad. Keep the file named rad for the whole run: Helm uses the executable name as the server-side-apply field manager, so upgrading with a renamed binary fails with conflict with "rad". The kit builds images, not the CLI; an older rad on PATH can apply dry-run chart values without installing cert-manager. Use exactly --skip-build only after a successful complete image build/push. The kit invokes a docker executable; with Podman, a temporary docker-to-podman symlink works (a shell alias does not). For the kit's HTTP local registry, configure only that endpoint (for example localhost:5003) as insecure inside the Podman VM; do not disable registry TLS globally. Preserve isolated HOME and unique cluster/registry names throughout the run. The full stack requires an A5 checkout; this A1 checkout only supplies the first layer.

Results

These are completed implementation runs; the reviewer's ongoing walkthrough is not claimed complete.

Check Result
go test ./pkg/cli/... (rerun after review fixes) ✅ pass
golangci-lint v2.14.0 on changed packages (rerun after review fixes) ✅ 0 issues
Fresh cluster with dryRun → cert-manager v1.21.2 installed with managed-by=rad label, Radius and Contour running ✅
Reinstall or plain install → rad-owned release reused or upgraded; label persists across revisions 1–4 ✅
rad upgrade (stored dryRun) → rad-owned cert-manager upgraded with Radius ✅
Uninstall → plan lists radius, contour, cert-manager; release and CRDs removed ✅
Rad-owned cert-manager plus a Certificate in namespace app, then uninstall → Keeping cert-manager: other workloads use it (3 resources, e.g. app/Certificate/web-tls); release and Certificate kept, uninstall succeeds ✅
CRDs left without a controller → clear "repair or remove" error, nothing installed ✅
Default install (mode off) → no cert-manager release, CRDs, or namespace ✅
User-installed v1.20.2 → reused on install and upgrade, still revision 1, kept on uninstall ✅
Fresh cluster with v1.19.4 → older than the minimum supported version v1.20.0, Radius not installed ✅
Reviewer cluster ownership inspection (October 21): Helm release Secret carries app.kubernetes.io/managed-by=rad; helm get metadata omits it ✅ verified
Rerun on a fresh kit cluster (October 21): mode off → no cert-manager; dryRun → v1.21.2 rev 1 with label; reinstall → rev 2, label kept; foreign Certificate → uninstall keeps cert-manager; after removing it → Uninstalling cert-manager..., release gone, CRD NotFound ✅

No contributor docs describe these install steps, so none were changed.

File change summary

File Summary of change
pkg/cli/helm/cert_manager.go New: version constants, mode resolution, cert-manager detection, install/reuse/upgrade/fail logic, ownership check, uninstall guarded by a foreign-resource check.
pkg/cli/helm/cluster.go ClusterOptions.CertManager/RadiusReleaseValues, InstallState.RadiusValues, Interface.CertManagerInstalledByRad; cert-manager wired into install, upgrade, and uninstall.
pkg/cli/helm/helmaction.go queryReleaseValues/getRelease expose stored values and labels; generic loadChart.
pkg/cli/helm/helmclient.go RunHelmInstall accepts release labels.
pkg/cli/helm/mock_*.go Regenerated.
pkg/cli/cmd/{install,upgrade}/kubernetes/kubernetes.go Pass stored Radius values to cluster options.
pkg/cli/cmd/uninstall/kubernetes/kubernetes.go List a rad-owned cert-manager in the uninstall plan.
pkg/cli/**/*_test.go New and updated table-driven tests for every table row, mode off, version from label vs image tag, ownership, and uninstall (including the foreign-resource guard and list-error fail-safe).

@sk593
sk593 requested review from a team as code owners October 19, 2026 20:29
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Dependency Review

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

Scanned Files

None

@github-actions github-actions Bot added the pr:waiting-for-review A reviewer owns the next action; required approval is pending or unverifiable label Oct 19, 2026
@sk593 sk593 added the pr:standard Ongoing maintenance, minor improvements, documentation updates, and routine development work label Oct 19, 2026
@sk593
sk593 marked this pull request as draft October 19, 2026 21:11
@github-actions github-actions Bot removed the pr:waiting-for-review A reviewer owns the next action; required approval is pending or unverifiable label Oct 19, 2026
@sk593
sk593 force-pushed the sk593-a1-cert-manager-install branch from f04ff56 to b1d74f9 Compare October 20, 2026 17:06
@sk593
sk593 force-pushed the sk593-a1-cert-manager-install branch from b1d74f9 to 0203ece Compare October 20, 2026 17:50
Base automatically changed from sk593-authz-manual-test-kit to sk593-authz-dry-run-ci-gate October 20, 2026 20:47
@sk593
sk593 deleted the branch sk593-authz-dry-run-ci-gate October 20, 2026 20:47
@sk593 sk593 closed this Oct 20, 2026
@sk593 sk593 reopened this Oct 20, 2026
@sk593
sk593 changed the base branch from sk593-authz-dry-run-ci-gate to main October 20, 2026 21:36
@sk593
sk593 force-pushed the sk593-a1-cert-manager-install branch from 0203ece to 6fc6499 Compare October 20, 2026 21:41
@sk593
sk593 added this pull request to stack #13372 October 20, 2026 21:51
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Unit Tests

    2 files  ± 0    424 suites  ±0   16m 13s ⏱️ - 1m 49s
7 482 tests +51  7 480 ✅ +51  2 💤 ±0  0 ❌ ±0 
8 652 runs  +52  8 650 ✅ +52  2 💤 ±0  0 ❌ ±0 

Results for commit dad67b0. ± Comparison against base commit b8b4578.

♻️ This comment has been updated with latest results.

@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.97425% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.37%. Comparing base (b8b4578) to head (dad67b0).

Files with missing lines Patch % Lines
pkg/cli/helm/cert_manager.go 79.42% 36 Missing ⚠️
pkg/cli/helm/helmaction.go 83.33% 5 Missing ⚠️
pkg/cli/helm/cluster.go 95.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #13344      +/-   ##
==========================================
+ Coverage   61.27%   61.37%   +0.10%     
==========================================
  Files         749      750       +1     
  Lines       44490    44711     +221     
==========================================
+ Hits        27261    27443     +182     
- Misses      17229    17268      +39     

☔ 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.

@sk593
sk593 marked this pull request as ready for review October 21, 2026 22:22
Copilot AI balanced review requested due to automatic review settings October 21, 2026 22:22
@github-actions github-actions Bot added the pr:waiting-for-review A reviewer owns the next action; required approval is pending or unverifiable label Oct 21, 2026
@sk593
sk593 force-pushed the sk593-a1-cert-manager-install branch from 6fc6499 to d19c713 Compare October 21, 2026 22:23

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.

🟡 Changes recommended

Authorization-mode resolution, owned-release validation, and purge error handling have unresolved correctness issues.

4 open issues
2 files not reviewed
  • pkg/cli/helm/mock_cluster.go: Generated file
  • pkg/cli/helm/mock_helmclient.go: Generated file
What changed in this PR

Adds cert-manager lifecycle management to the Radius CLI as the first layer of component mTLS support. Authorization-enabled installations gain prerequisite setup without adding new flags.

Changes:

  • Install, reuse, or upgrade cert-manager based on ownership and supported versions.
  • Use stored Radius values when determining whether authorization requires cert-manager.
  • Include rad-owned cert-manager in uninstall planning and retain it when other workloads depend on it.
File Description
pkg/​cli/​helm/​mock_helmclient.go Updates generated mocks for release labels.
pkg/​cli/​helm/​mock_cluster.go Adds the ownership-check mock.
pkg/​cli/​helm/​helmclient.go Supports Helm release labels during installation.
pkg/​cli/​helm/​helmclient_test.go Updates install calls for the new signature.
pkg/​cli/​helm/​helmaction.go Exposes stored values and release metadata; adds chart loading helper.
pkg/​cli/​helm/​helmaction_test.go Updates Helm install expectations.
pkg/​cli/​helm/​cluster.go Integrates cert-manager into cluster lifecycle operations.
pkg/​cli/​helm/​cluster_test.go Covers stored values and updated lifecycle calls.
pkg/​cli/​helm/​cert_manager.go Implements detection, ownership, mode resolution, and guarded removal.
pkg/​cli/​helm/​cert_manager_test.go Tests prerequisite decisions and uninstall safeguards.
pkg/​cli/​cmd/​upgrade/​kubernetes/​kubernetes.go Passes stored Radius values into upgrade options.
pkg/​cli/​cmd/​upgrade/​kubernetes/​kubernetes_test.go Verifies stored-value forwarding during upgrade.
pkg/​cli/​cmd/​uninstall/​kubernetes/​kubernetes.go Adds rad-owned cert-manager to the confirmation plan.
pkg/​cli/​cmd/​uninstall/​kubernetes/​kubernetes_test.go Updates ownership expectations and plan output.
pkg/​cli/​cmd/​install/​kubernetes/​kubernetes.go Passes stored Radius values into install options.
pkg/​cli/​cmd/​install/​kubernetes/​kubernetes_test.go Verifies stored-value forwarding during reinstall.

Review effort: Balanced

Comment thread pkg/cli/cmd/uninstall/kubernetes/kubernetes.go
Comment thread pkg/cli/helm/cert_manager.go
Comment thread pkg/cli/helm/cert_manager.go Outdated
Comment thread pkg/cli/helm/cert_manager.go Outdated
@github-actions github-actions Bot added pr:waiting-for-author The author needs to address human review feedback or human-endorsed inline feedback pr:waiting-for-review A reviewer owns the next action; required approval is pending or unverifiable and removed pr:waiting-for-review A reviewer owns the next action; required approval is pending or unverifiable pr:waiting-for-author The author needs to address human review feedback or human-endorsed inline feedback labels Oct 21, 2026
sk593 and others added 7 commits October 22, 2026 15:51
Add failing tests for detecting cert-manager, installing a pinned
version when it is missing and authorization is enabled, reusing a
supported user installation, rejecting versions older than the minimum,
and upgrading or uninstalling only releases rad owns.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
When global.rbac.enabled or global.rbac.dryRun is set, rad install and
rad upgrade kubernetes detect cert-manager before installing Radius.
They install a pinned cert-manager (v1.21.2) when none exists, reuse a
supported one (>= v1.20.0), and fail on an older one. A cert-manager
rad installs carries the Helm release label
app.kubernetes.io/managed-by=rad; only such releases are upgraded or
uninstalled by rad. With authorization off nothing changes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
List a rad-installed cert-manager in the rad uninstall plan so users see
it before confirming, and install it with crds.keep=false so uninstall
also removes its CRDs. Otherwise a later install with authorization
enabled finds cert-manager CRDs without a controller and stops.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
Removing a rad-installed cert-manager deletes its CRDs, which deletes
every Certificate, CertificateRequest, Issuer and ClusterIssuer in the
cluster. Before uninstalling it, list those resources and keep
cert-manager when any exist outside the Radius namespace, or when the
check fails. Uninstall still succeeds and prints why it kept it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
…ails

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
…uthiness rules

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
…ts controller

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: sk593 <shruthikumar@microsoft.com>
@sk593
sk593 force-pushed the sk593-a1-cert-manager-install branch from 3a837d9 to dad67b0 Compare October 22, 2026 22:51
@radius-functional-tests

radius-functional-tests Bot commented Oct 22, 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 dad67b0
Unique ID func571b8b9fd8
Image tag test-func571b8b9fd8-39867886624-1
  • 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>:test-func571b8b9fd8-39867886624-1
  • 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:test-func571b8b9fd8-39867886624-1
  • dynamic-rp test image location: ghcr.io/radius-project/dev/dynamic-rp:test-func571b8b9fd8-39867886624-1
  • controller test image location: ghcr.io/radius-project/dev/controller:test-func571b8b9fd8-39867886624-1
  • ucp test image location: ghcr.io/radius-project/dev/ucpd:test-func571b8b9fd8-39867886624-1
  • 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

This branch has not been deployed

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

Labels

pr:standard Ongoing maintenance, minor improvements, documentation updates, and routine development work pr:waiting-for-review A reviewer owns the next action; required approval is pending or unverifiable

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants