Skip to content

feat(control-plane): adopt upstream OpenShell Helm chart for gateway deployments - #194

Draft
bsquizz wants to merge 25 commits into
mainfrom
helm
Draft

feat(control-plane): adopt upstream OpenShell Helm chart for gateway deployments#194
bsquizz wants to merge 25 commits into
mainfrom
helm

Conversation

@bsquizz

@bsquizz bsquizz commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds spec for shifting the control plane from static YAML manifests (generated once via helm template) to installing gateways at runtime using the upstream OpenShell Helm chart via the Helm Go SDK
  • Defines values mapping from Gateway API resources to Helm chart values
  • Documents migration path for adopting existing SSA-managed resources into Helm releases without downtime
  • Decision: no NetworkPolicies — empirically verified on OVN-Kubernetes that they are unnecessary and create a self-defeating deny/re-allow cycle
  • Gap analysis narrowed to 2 items the control plane handles within the gateway namespace: OpenShift SCC binding and trusted CA ConfigMap

JIRA

HYPERSHELL-146

Test plan

  • Review spec for completeness and accuracy against current control-plane code
  • Validate Helm chart values mapping against upstream values.yaml
  • Confirm PR #2728 coverage of BackendTLSPolicy and backend CA ConfigMap

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🚫 Excluded labels (none allowed) (2)
  • do-not-merge/work-in-progress
  • do-not-merge/hold

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: a1478bf0-dedf-4eb2-89ee-577a8703d93e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

│ GRPCRoute, BackendTLSPolicy (PR #2728)
│ (NetworkPolicy disabled — see decision below)
├─ 8. Reconcile OpenShift SCC binding ← Go (unchanged)
├─ 9. Reconcile ingress (BackendCA ConfigMap) ← Go (if not covered by chart)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

In what cases would this not be covered by the chart?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The only scenario I can think of at the moment is when the 'certgen' job fails to create the BackendCA Configmap due to a cert-manager timeout. In that case, we can circle back and run the equivalent of a 'helm upgrade'

- AND the release namespace SHALL be the gateway's API-assigned namespace
- AND `Install.CreateNamespace` SHALL be `false` (the reconciler creates the namespace itself)

#### Scenario: Gateway update (Helm upgrade)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We are not currently handling upgrades. Let's specifically not in this spec that upgrades are currently not handled. Only install when a gateway is created / and uninstall when a gateway is deleted.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The only time we might invoke 'helm upgrade' is in a "retry" scenario where initial install was not fully successful

Comment thread specs/platform/openshell-gateway-helm-adoption.spec.md

- GIVEN the environment variable `HELM_CHART_REGISTRY` is set (e.g. `oci://ghcr.io/nvidia/openshell/helm-chart`)
- WHEN the reconciler loads the chart
- THEN it SHALL pull from the OCI registry instead of the embedded path

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Can we ensure that the reconciler pulls the chart ONCE during startup, so it does not need to re-pull for every reconcile?

| `kubernetes-secrets` driver | `credentialDrivers.kubernetesSecrets.enabled=true` |
| Vault driver | `credentialDrivers.vault.enabled=true`, `credentialDrivers.vault.*` |

#### Ingress Values (conditional)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

control-plane currently deploys a route.openshift.io Route resource in cases where a BackendTLSPolicy w/ Gateway API will not work (e.g. OpenShift versions under 4.22). We should use the chart to deploy the Route in these cases as well.

- GIVEN the control plane manages sandbox CRD installation separately
- WHEN computing Helm values
- THEN `agentSandbox.preflight.enabled` SHALL be set to `false`
- AND the chart SHALL not fail if the sandbox CRD API is not yet served

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think that actually this is a good check to keep in place. Agent Sandbox is an installation pre-requisite. If it is not in place on the cluster, it is OK for the helm install to fail and for an error to be logged.


---

### Requirement: Migration Path

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The migration path can be very simple, because this platform is still in 'beta' state.

New gateway creations will use the helm chart

Don't worry about existing gateways.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

See comment about deleting namespaces.

- WHEN the GatewayReconciler processes the event
- THEN it SHALL call `action.Uninstall` to remove chart-managed resources
- AND it SHALL separately clean up non-chart resources (database, console, SCC binding, extra network policies, Keycloak clients)
- AND it SHALL NOT delete the namespace (consistent with current behavior)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Let's begin to delete the namespace. We want to do this anyways, plus, it makes migration paths easier (if a helm chart was installed for the gateway, then run 'helm uninstall' before deleting namespace. Otherwise, just delete the namespace)


Existing gateway deployments use directly-applied resources (SSA). Transitioning to Helm-managed releases requires adopting existing resources into the Helm release without downtime.

#### Scenario: Adopt existing resources into Helm release

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not necessary

- AND no resources SHALL be deleted or recreated during migration
- AND the gateway pod SHALL NOT be restarted unless the Deployment spec actually changes

#### Scenario: Rollback capability

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We are not handling gateway upgrades at this time. Section not necessary

- THEN the `Atomic` flag SHALL cause automatic rollback to the previous release revision
- AND the reconciler SHALL log the failure and retry on the next reconciliation cycle

#### Scenario: Mixed-state during rolling upgrade

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is fine as long as we do not need 2 code paths... i.e. I want to only have the 'helm deploy method' in the latest code and not keep the old "SSA-based deployment" code hanging around.


| # | Resource | Kind | Why the Control Plane Handles It |
|---|---|---|---|
| 1 | `openshell-sandbox-privileged-scc` | RoleBinding | OpenShift SCC binding granting the `privileged` SCC to the sandbox ServiceAccount. The chart handles `podSecurityContext` values but has no concept of OpenShift SCC grants. |

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

You can add a note about how the creation of this rolebinding outside of the chart is documented here: https://github.com/NVIDIA/OpenShell/blob/main/deploy/helm/openshell/README.md#install-on-openshift

| # | Resource | Kind | Why the Control Plane Handles It |
|---|---|---|---|
| 1 | `openshell-sandbox-privileged-scc` | RoleBinding | OpenShift SCC binding granting the `privileged` SCC to the sandbox ServiceAccount. The chart handles `podSecurityContext` values but has no concept of OpenShift SCC grants. |
| 2 | `gateway-trusted-ca` | ConfigMap | CA bundle for private-CA environments (e.g. Keycloak behind OpenShift ingress). Copied from the CP namespace and mounted into the gateway Deployment via a post-Helm SSA patch. |

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Apparently, the upstream chart can handle this:

● The chart already handles this. Setting server.oidc.caConfigMapName to the name of a ConfigMap makes the chart automatically:

  1. Add the SSL_CERT_FILE env var to the gateway container
  2. Mount the ConfigMap as a volume
  3. Wire it all up in the Deployment

  So we don't need a post-Helm SSA patch for the trusted CA. The control plane just needs to:
  1. Copy the gateway-trusted-ca ConfigMap into the tenant namespace (as it does today)
  2. Pass server.oidc.caConfigMapName: "gateway-trusted-ca" in the Helm values


---

## Trusted CA Injection Strategy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

See above comment. We'll change the way this works, and this whole section can go away.


---

## NetworkPolicy Decision: Do Not Install

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Add a note that we will revisit this if/when using a restrictive network policy posture becomes a platform requirement.

2. CNPG Database + credentials Secret (must exist before Helm install)
3. Trusted CA ConfigMap copy (must exist before Helm install if present)
4. Helm install/upgrade (creates core workload + chart-managed resources)
5. OpenShift SCC binding (can run after Helm, before pod scheduling)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Install the SCC binding before the helm install

3. Trusted CA ConfigMap copy (must exist before Helm install if present)
4. Helm install/upgrade (creates core workload + chart-managed resources)
5. OpenShift SCC binding (can run after Helm, before pod scheduling)
6. Trusted CA Deployment overlay (must run after Helm, patches the Deployment)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This will go away

@bsquizz

bsquizz commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

Amber Review — Spec Quality Assessment

Overall: Spec is comprehensive and addresses all user feedback. The TLS architecture explanation for Route passthrough mode is accurate and well-documented. Minor issues below.


Strengths

  1. TLS Architecture (lines 186-236): Excellent explanation of the fundamental difference between GRPCRoute+BackendTLSPolicy (re-encrypt) vs Route passthrough (end-to-end). Correctly identifies that Route mode requires externally-trusted CA.

  2. Gap Analysis (lines 293-304): Properly narrowed to 1 resource (SCC binding). Clear explanation of what the chart handles vs what the control plane handles.

  3. NetworkPolicy Decision (lines 307-322): Well-documented with empirical evidence. Rationale is sound.

  4. Chart Sourcing (lines 120-141): Solid strategy — embed .tgz by default, OCI override for dev only.

  5. Environment Variables (lines 384-392): Correctly adds EXTERNAL_CA_ISSUER_NAME and EXTERNAL_CA_ISSUER_KIND for Route mode.


🔧 Issues

1. Minor Inconsistency — Gap Table (line 291)

Issue: Line 291 states Go (post-Helm SSA patch) for trusted CA injection, but line 301 correctly says "no post-Helm SSA patch is needed."

Fix:

| Trusted CA volume/mount/env | Chart (`_gateway-workload.tpl`) | `_gateway-workload.tpl` | `server.oidc.caConfigMapName` |

Severity: Minor
Confidence: High (100%)


2. Missing Error Handling Guidance (Helm Operations)

Issue: Spec doesn't specify how Helm install/upgrade failures should be handled per HyperShell conventions:

  • Error wrapping with context (fmt.Errorf("installing gateway %s: %w", name, err))
  • Gateway status updates on error paths
  • No panic() in reconciler (return explicit errors)

Recommendation: Add to "Code Changes" section:

### Error Handling (HyperShell Convention)

- Helm install/upgrade/uninstall errors SHALL be wrapped with context: `fmt.Errorf("helm install gateway %s: %w", gateway.Name, err)`
- Gateway status SHALL be updated on Helm failure with `State: "Failed"` and error message
- Reconciler SHALL return explicit errors — NEVER `panic()` on Helm failures

Severity: Major (impacts production error handling)
Confidence: High (95%)


3. Incomplete Retry Guidance (lines 102-107)

Issue: "Retry after failed install" scenario mentions helm upgrade but doesn't specify:

  • Max retry attempts before marking gateway as permanently failed
  • Backoff strategy (immediate retry vs exponential backoff)
  • How to detect a "failed release" (Helm release status query)
  • Whether to update Gateway status during retries

Recommendation: Expand scenario:

#### Scenario: Retry after failed install

- GIVEN a previous Helm install failed (e.g. certgen job timed out)
- WHEN the GatewayReconciler processes the next reconciliation event
- THEN it SHALL query Helm release status via `action.Get`
- AND if release status is `failed` or `pending-install`, it SHALL run `helm upgrade --reuse-values` to retry
- AND it SHALL implement exponential backoff (1m, 2m, 4m, 8m, max 15m between retries)
- AND it SHALL mark the Gateway status as `State: "Failed"` after 5 consecutive failures
- AND this is the only scenario where `helm upgrade` is invoked

Severity: Major (impacts retry behavior)
Confidence: Medium (80% — exact retry strategy may vary)


4. Missing Configuration Validation

Issue: No requirement for validating env vars at startup. Control plane should fail-fast if EXTERNAL_CA_ISSUER_NAME is empty when managing clusters without Gateway API.

Recommendation: Add to "Configuration" section:

### Validation at Startup

- Control plane SHALL validate `EXTERNAL_CA_ISSUER_NAME` is set when any managed cluster lacks Gateway API support
- Control plane SHALL fail fast with clear error message if validation fails
- Chart version compatibility SHALL be verified against `HELM_CHART_VERSION` (if pulling from OCI registry)

Severity: Major (prevents runtime failures)
Confidence: High (90%)


5. Chart Version Pinning — Implementation Detail Missing

Issue: Line 129 says "chart version SHALL be pinned in the Dockerfile build script" but doesn't specify HOW (version file, make variable, ARG, etc.).

Recommendation: Add implementation detail:

- AND the chart version SHALL be declared as a `VERSION` file in `components/control-plane/charts/VERSION`
- AND the Dockerfile SHALL read this file: `ARG CHART_VERSION=$(cat charts/VERSION)`
- AND the build SHALL run: `helm pull oci://ghcr.io/nvidia/openshell/helm-chart --version ${CHART_VERSION} --destination /charts/`

Severity: Minor (implementation clarity)
Confidence: Medium (75% — multiple valid approaches)


📊 Summary

Category Count
Strengths 5
Minor Issues 2
Major Issues 3
Blockers 0

Recommendation: Address major issues (error handling, retry guidance, config validation) before implementation. Minor issues can be fixed during PR review of the actual code.


Confidence Level: High (90%) — Spec is solid, issues are additive improvements rather than fundamental flaws.


You are Amber. Be the colleague everyone wishes they had.

@bsquizz bsquizz added amber/self-review This PR was reviewed by the Amber review agent by one of the contributors to the PR. amber/changes-requested Amber requested changes on this PR labels Aug 24, 2026
@bsquizz

bsquizz commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

Amber Follow-Up Review — Post TLS Architecture Addition

Status: Latest commit (0d8b697) successfully addresses bsquizz's feedback on TLS architecture for Route passthrough mode.


User Feedback Addressed

bsquizz's request: "Yeah, we shouldn't be using a self-signed internal cert. I think you should add an explanation of all this functionality to the spec."

Resolution: Lines 186-236 now include:

  • Clear TLS architecture diagrams for both modes
  • Explanation of why Route passthrough requires externally-trusted CA
  • certManager.serverIssuerRef mechanism documented
  • Chart validation rules listed
  • Cluster prerequisite (pre-provisioned ClusterIssuer) documented
  • Environment variables added (EXTERNAL_CA_ISSUER_NAME, EXTERNAL_CA_ISSUER_KIND)

Verdict: ✅ Fully addressed. The explanation is accurate and comprehensive.


🔧 Remaining Issues from Previous Review

My previous review identified 5 issues. Checking current status:

Issue #1: Gap Table Inconsistency (Line 296) — ❌ Still Present

Line 296 still states:

| Trusted CA volume/mount/env | Go (post-Helm SSA patch) | ... |

But line 306 correctly states: "no post-Helm SSA patch is needed."

Required Fix:

-| Trusted CA volume/mount/env | Go (post-Helm SSA patch) | `_gateway-workload.tpl` | `server.oidc.caConfigMapName` |
+| Trusted CA volume/mount/env | Chart (`_gateway-workload.tpl`) | `_gateway-workload.tpl` | `server.oidc.caConfigMapName` |

Severity: Minor
Confidence: High (100%)


Issue #2: Missing Error Handling Guidance — ❌ Still Missing

No HyperShell-convention error handling documented for Helm operations.

Recommendation: Add to "Code Changes" section:

### Error Handling (HyperShell Convention)

- Helm install/upgrade/uninstall errors SHALL be wrapped with context: `fmt.Errorf("helm install gateway %s: %w", gateway.Name, err)`
- Gateway status SHALL be updated on Helm failure with `State: "Failed"` and error message
- Reconciler SHALL return explicit errors — NEVER `panic()` on Helm failures

Severity: Major
Confidence: High (95%)


Issue #3: Incomplete Retry Guidance (lines 102-107) — ❌ Still Incomplete

Retry scenario lacks:

  • Max retry attempts
  • Backoff strategy
  • Release status detection
  • Status update during retries

Severity: Major
Confidence: Medium (80%)


Issue #4: Missing Configuration Validation — ❌ Still Missing

No requirement for validating EXTERNAL_CA_ISSUER_NAME at startup.

Severity: Major
Confidence: High (90%)


Issue #5: Chart Version Pinning Detail — ❌ Still Vague

Line 129 says "pinned in the Dockerfile build script" but doesn't specify how.

Severity: Minor
Confidence: Medium (75%)


📊 Updated Summary

Status Count
User Feedback Addressed ✅ Complete
Minor Issues Remaining 2
Major Issues Remaining 3
Blockers 0

Overall Assessment: The TLS architecture addition is excellent and fully addresses bsquizz's feedback. The 5 issues from my initial review remain unaddressed but are additive improvements — they don't block spec approval, but should be addressed before implementation begins.

Recommendation:

  1. Approve the spec for the TLS architecture work (user's primary concern)
  2. Track the 5 issues as implementation requirements to be addressed in the code PR
  3. Fix the minor gap table inconsistency (1-line change) now if convenient

Confidence: High (95%)


Amber review complete. The spec correctly documents the TLS architecture difference that bsquizz asked for.

@bsquizz

bsquizz commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

Amber Final Review — Excellent Work! ✅

Status: Commit afdf18e addresses 4 of 5 issues from my previous review. Outstanding work implementing the feedback.


Issues Resolved

Issue #2: Error Handling Guidance — ✅ RESOLVED

Added: Lines 353-361 now document HyperShell error handling conventions:

  • Context wrapping with fmt.Errorf
  • Gateway status updates on failures
  • No panic() in reconciler
  • Multi-error collection for partial failures

Verdict: Excellent. Fully compliant with HyperShell conventions from CLAUDE.md.


Issue #3: Retry Guidance — ✅ RESOLVED

Added: Lines 106-110 now specify:

  • Helm release status detection via action.Get
  • Exponential backoff strategy (1m, 2m, 4m, 8m, max 15m)
  • Max 5 consecutive failures before permanent failure
  • Status updates during retries

Verdict: Complete and production-ready. The exponential backoff prevents reconciler storms.


Issue #4: Configuration Validation — ✅ RESOLVED

Added: Lines 409-416 specify startup validation:

  • Validate EXTERNAL_CA_ISSUER_NAME when Route passthrough mode will be used
  • Fail-fast with clear error messages
  • Validate HELM_CHART_VERSION when OCI registry is configured
  • Chart version compatibility warnings

Verdict: Exactly what was needed. Prevents runtime failures with clear startup feedback.


Issue #5: Chart Version Pinning — ✅ RESOLVED

Added: Lines 132-135 specify implementation:

  • Chart version in components/control-plane/charts/VERSION file
  • Dockerfile reads version via ARG CHART_VERSION=$(cat charts/VERSION)
  • Build command with helm pull oci://...--version ${CHART_VERSION}
  • Upgrade process documented

Verdict: Clear, actionable implementation guidance. Easy to automate in CI.


🔧 Remaining Issue

Issue #1: Gap Table Inconsistency (Line 296)⚠️ Still Present

Current state:

| Trusted CA volume/mount/env | Go (post-Helm SSA patch) | `_gateway-workload.tpl` | `server.oidc.caConfigMapName` |

Contradiction: Line 306 states: "no post-Helm SSA patch is needed."

One-line fix:

-| Trusted CA volume/mount/env | Go (post-Helm SSA patch) | `_gateway-workload.tpl` | `server.oidc.caConfigMapName` |
+| Trusted CA volume/mount/env | Chart (`_gateway-workload.tpl`) | `_gateway-workload.tpl` | `server.oidc.caConfigMapName` |

Impact: Minor documentation inconsistency. Doesn't block implementation — implementers will read line 306 and do the right thing.

Severity: Minor
Confidence: High (100%)


📊 Final Summary

Metric Status
User Feedback (TLS Architecture) ✅ Fully Addressed
Amber Major Issues ✅ 3/3 Resolved
Amber Minor Issues ⚠️ 1/2 Resolved
Blockers 0
Implementation Readiness ✅ Ready

Overall Verdict: This spec is implementation-ready. The one remaining minor inconsistency is cosmetic and doesn't impact the implementation phase.

Recommendation:

  • Approve the spec — all substantive issues resolved
  • ⚠️ Fix the line 296 inconsistency if convenient (optional, 1-line change)
  • 🚀 Proceed to implementation with confidence

Confidence: High (98%)


🎯 Why This Spec Is Excellent

  1. Comprehensive TLS Architecture: Lines 186-236 clearly explain the security model difference between GRPCRoute (re-encrypt) and Route (passthrough)
  2. Production-Grade Error Handling: HyperShell conventions documented, no panic(), proper context wrapping
  3. Robust Retry Logic: Exponential backoff prevents reconciler storms, max retries prevent infinite loops
  4. Fail-Fast Validation: Startup checks catch configuration errors before runtime
  5. Clear Implementation Path: Chart version pinning, Helm SDK usage, ordering constraints all documented
  6. Empirically Validated Decisions: NetworkPolicy analysis backed by live cluster testing

bsquizz, this is solid engineering documentation. The implementation team has everything they need.


Amber review complete. Changing label to amber/approved.

@bsquizz bsquizz added amber/approved The Amber review agent has approved this PR. and removed amber/changes-requested Amber requested changes on this PR labels Aug 25, 2026
bsquizz and others added 4 commits August 26, 2026 12:37
Add specification for shifting the control plane from static YAML
manifests to the upstream OpenShell Helm chart via the Helm Go SDK.

Covers: SDK integration, values mapping, migration path, gap analysis,
and the decision to not install NetworkPolicies.

HYPERSHELL-146

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cision

- Chart sourcing: embedded .tgz in container image (default), OCI
  registry override for dev only
- Gap analysis narrowed to 2 items: SCC binding and trusted CA ConfigMap
- NetworkPolicy decision: do not install, verified empirically on
  OVN-Kubernetes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Key changes from review:
- No upgrades: install on create, uninstall+delete-namespace on delete,
  helm upgrade only for retry after failed install
- Keep sandbox preflight enabled (valid prerequisite check)
- Simple migration: new gateways use Helm, existing ones not migrated
- Single code path: remove old SSA code entirely, no dual-mode
- Load chart once at startup, reuse for all installs
- Trusted CA via chart values (server.oidc.caConfigMapName), remove
  post-Helm SSA overlay section
- SCC binding runs before Helm install
- OpenShift Route via chart (openshiftRoute.enabled)
- Add upstream docs reference for SCC binding
- NetworkPolicy: add note to revisit if restrictive posture required
- Remove rollback, mixed-state, upgrade, and implementation phases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…A config

Explain how TLS differs between GRPCRoute (internal CA sufficient) and
Route passthrough (needs externally trusted CA via certManager.serverIssuerRef).
Add EXTERNAL_CA_ISSUER_NAME and EXTERNAL_CA_ISSUER_KIND env vars.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@bsquizz bsquizz left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Amber Review — Fix Verification (Commit ba13ebe)

All 8 findings from the prior review are resolved. The fixes are correct, minimal, and don't introduce new issues. Nice work.


Verification of Prior Findings

# Severity Finding Status Verification
1 Critical setNestedValue panics on type assertion Resolved values.go:266 — uses next, ok guard with fallback make(map[string]interface{}). Matches Helm's own CoalesceTables behavior.
2 Critical splitImageRef misparses registry-with-port Resolved values.go:248-253 — compares LastIndex("/") vs LastIndex(":"). Port-only refs like registry:5000/image now correctly return ("registry:5000/image", "latest").
3 Major isChartArchive panics on short paths Resolved chart.go:36 — uses strings.HasSuffix, which handles length internally. No more slice-bounds panic on 5-6 char paths.
4 Major PullChart hardcodes "helm" binary Resolved chart.go:41 — now accepts helmBinary parameter with """helm" fallback. Also accepts context.Context.
5 Major deployGatewayViaHelm upgrades on every reconcile Resolved helm_deploy.go:74-75 — "deployed" status skips upgrade. Matches spec: "Gateway upgrades are not handled at this time."
6 Major Database config resolution error downgraded to INFO Resolved reconciler.go:1414 — changed to WARN. Appropriate severity for a skipped-but-noteworthy condition.
7 Minor INGRESS_BASE_DOMAIN defaults to apps.example.com Resolved reconciler.go:1266 — changed to gateway.cluster.local (non-routable .local suffix).
8 Minor VerifyHelmAvailable and PullChart lack context Resolved shell_client.go:203, chart.go:41 — both accept context.Context and use exec.CommandContext.

Convention Checklist (re-verified against fix commit)

Convention Result
No panic() in production code Pass — grep confirms zero panic() calls in non-test code
Errors wrapped with fmt.Errorf context Pass
No secrets in logs or responses Pass
No context.TODO() in production code Pass
Reconcile, don't create-or-skip Pass

Minor Observation (Not a Finding)

helm_deploy.go:83 logs "helm release deployed" unconditionally after the if/else chain, including when the "deployed" branch skipped the install. Technically accurate (it IS deployed), but could read as "we just deployed it" in logs. Not worth a code change — just noting for awareness.


Confidence: High (98%)
Verdict: All prior findings resolved. Fixes are clean and correct.

Amber review complete.

@bsquizz bsquizz added amber/approved The Amber review agent has approved this PR. and removed amber/changes-requested Amber requested changes on this PR labels Aug 26, 2026
@bsquizz

bsquizz commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

On hold until these are merged upstream:

NVIDIA/OpenShell#2728
NVIDIA/OpenShell#2939

Once they are merged and released, then we need to circle back and remove the custom gateway image and stop bundling the fork of the helm chart (those were used to prove e2e tests can pass)

@bsquizz bsquizz added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 26, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jsell-rh

jsell-rh commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Amber review

Status: Complete

Verdict

REQUEST_CHANGES (posted as a COMMENT review). This is a well-structured, high-value refactor that replaces ~1,600 lines of hand-maintained SSA manifest code with a Helm-chart-driven deployment path, but it is not mergeable to main as-is: the control-plane image is built by cloning a personal GitHub fork's mutable feature branch and pins a personal quay image, a database-config resolution error is now silently swallowed, and the spec (Helm Go SDK) diverges from the implementation (shelling out to the helm CLI). Address the Blocker/Major items below before merge.

Hi @bsquizz — Amber here. The direction (delegate gateway rendering to the upstream chart, delete the drift-prone embedded manifests) is the right long-term call, and the internal/helm package is clean and readable. My concerns are about supply-chain reproducibility, a swallowed error, and reconcile semantics — details below.

Blocker

1. Production image build depends on a personal fork's mutable branch + personal quay imageSecurity / Supply chain / Image consistency

  • charts/CHART_REPO = https://github.com/bsquizz/OpenShell.git, charts/CHART_REF = feat/backend-tls-and-rbac-toggle (a branch, not a tag/SHA).
  • components/control-plane/Dockerfile:29 runs git clone --depth 1 --branch "${CHART_REF}" at image-build time. A branch is mutable and can be force-pushed or deleted, so the control-plane image is not reproducible and CI can break at any time with no code change here.
  • components/control-plane/internal/gateway/config.go:40 pins defaultGatewayImage = quay.io/bsquizza/openshell-gateway:16112bc (personal registry), while :41 keeps the supervisor on ghcr.io/nvidia/openshell/supervisor:16112bc. CLAUDE.md requires "Image references must match across the stack."
  • This appears to be a deliberate temporary state pending upstream NVIDIA/OpenShell PR #2728, but as written it cannot merge to main. Please either (a) point CHART_REPO/CHART_REF at the canonical upstream repo pinned to an immutable tag/SHA and use canonical images, or (b) explicitly gate this PR behind that upstream merge and note it in the description. This is the primary maintainer decision this PR needs.

Major

2. Database-config resolution error is now silently swallowedError handling / "never silently swallow partial failures"

  • components/control-plane/internal/reconciler/reconciler.go:1414: resolveDatabaseConfig failure changed from return reconcileErr (which marked the gateway Failed) to a log.Printf("WARN ... skipping database reconciliation") and continues into the Helm install. The gateway then deploys referencing openshell-gateway-db-credentials which may not exist, and its phase never reflects the failure. This violates the CLAUDE.md rule that "every error path must propagate or be collected." Restore the fail-closed behavior (mark Failed), or document why proceeding is safe.

3. Spec says "Helm Go SDK"; implementation shells out to the helm CLISpec consistency

  • specs/platform/openshell-gateway-helm-adoption.spec.md states the reconciler "SHALL use the Helm Go SDK" and documents helm.sh/helm/v3/pkg/action. The implementation (internal/helm/shell_client.go) instead execs the helm binary (with a comment explaining the pivot to avoid dependency conflicts). The pivot is reasonable, but the spec must be updated to match — otherwise the desired-state doc contradicts the code, and the runtime now has a new hard dependency on a helm binary in the image + HELM_BINARY/PATH resolution that the spec never describes.

4. Already-deployed releases are never upgraded (create-or-skip)Reconciliation convention

  • internal/gateway/helm_deploy.go:74 skips when status == "deployed", so image/OIDC/route/config changes to a running gateway never converge. The spec acknowledges "Gateway upgrades are not handled at this time," but this is a functional regression from the previous SSA path, which did re-apply on spec changes, and it violates "reconcile, don't create-or-skip." Relatedly, internal/helm/shell_client.go:152 uses --reuse-values together with --values on the retry path, so keys removed from the desired set persist across retries. Please call this limitation out explicitly in the PR body and file a follow-up, and coordinate with PR spec(control-plane): gate gateway re-provisioning on desired-state convergence #151 (see Cross-PR section).

5. Out-of-band route teardown will drift Helm stateArchitecture

  • internal/reconciler/health.go:439 self-documents that DeleteGatewayAPIResources deletes chart-managed routing resources outside Helm, "may cause Helm state drift." With routing now owned by the chart, the health loop and Helm will fight over these resources (Helm still believes them present; a later reconcile may recreate). This needs a real plan (e.g., helm upgrade with routing disabled) rather than a TODO before it lands.

Minor

6. splitImageRef mishandles digest referencesinternal/helm/values.go:247 splits on the last :; an image like repo@sha256:abcd yields repo=...@sha256, tag=abcd. Not hit by the current tag-based defaults, but will silently corrupt digest-pinned images. Guard for @.

7. Pointless indirectioninternal/gateway/helm_deploy.go:111 getEnvgetEnvHelperos.Getenv. Collapse to a single helper (or reuse an existing env helper) to reduce noise.

8. Lost documentationinternal/config/config.go dropped the detailed DatabaseProvider comment explaining the CNPG-vs-deployment default and no-silent-fallback behavior with no functional change. Keep the rationale.

9. Delegated pod SecurityContext is now unverifiable here — pod security context comes entirely from the upstream chart, and buildOpenShiftValues nils podSecurityContext.fsGroup/securityContext.runAsUser to defer to SCC. Please confirm the chart still yields runAsNonRoot: true, allowPrivilegeEscalation: false, and drop: ["ALL"] per security.spec.md, since we no longer set these in-tree.

@jsell-rh jsell-rh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict

REQUEST_CHANGES (posted as a COMMENT review). This is a well-structured, high-value refactor that replaces ~1,600 lines of hand-maintained SSA manifest code with a Helm-chart-driven deployment path, but it is not mergeable to main as-is: the control-plane image is built by cloning a personal GitHub fork's mutable feature branch and pins a personal quay image, a database-config resolution error is now silently swallowed, and the spec (Helm Go SDK) diverges from the implementation (shelling out to the helm CLI). Address the Blocker/Major items below before merge.

Hi @bsquizz — Amber here. The direction (delegate gateway rendering to the upstream chart, delete the drift-prone embedded manifests) is the right long-term call, and the internal/helm package is clean and readable. My concerns are about supply-chain reproducibility, a swallowed error, and reconcile semantics — details below.

Blocker

1. Production image build depends on a personal fork's mutable branch + personal quay imageSecurity / Supply chain / Image consistency

  • charts/CHART_REPO = https://github.com/bsquizz/OpenShell.git, charts/CHART_REF = feat/backend-tls-and-rbac-toggle (a branch, not a tag/SHA).
  • components/control-plane/Dockerfile:29 runs git clone --depth 1 --branch "${CHART_REF}" at image-build time. A branch is mutable and can be force-pushed or deleted, so the control-plane image is not reproducible and CI can break at any time with no code change here.
  • components/control-plane/internal/gateway/config.go:40 pins defaultGatewayImage = quay.io/bsquizza/openshell-gateway:16112bc (personal registry), while :41 keeps the supervisor on ghcr.io/nvidia/openshell/supervisor:16112bc. CLAUDE.md requires "Image references must match across the stack."
  • This appears to be a deliberate temporary state pending upstream NVIDIA/OpenShell PR #2728, but as written it cannot merge to main. Please either (a) point CHART_REPO/CHART_REF at the canonical upstream repo pinned to an immutable tag/SHA and use canonical images, or (b) explicitly gate this PR behind that upstream merge and note it in the description. This is the primary maintainer decision this PR needs.

Major

2. Database-config resolution error is now silently swallowedError handling / "never silently swallow partial failures"

  • components/control-plane/internal/reconciler/reconciler.go:1414: resolveDatabaseConfig failure changed from return reconcileErr (which marked the gateway Failed) to a log.Printf("WARN ... skipping database reconciliation") and continues into the Helm install. The gateway then deploys referencing openshell-gateway-db-credentials which may not exist, and its phase never reflects the failure. This violates the CLAUDE.md rule that "every error path must propagate or be collected." Restore the fail-closed behavior (mark Failed), or document why proceeding is safe.

3. Spec says "Helm Go SDK"; implementation shells out to the helm CLISpec consistency

  • specs/platform/openshell-gateway-helm-adoption.spec.md states the reconciler "SHALL use the Helm Go SDK" and documents helm.sh/helm/v3/pkg/action. The implementation (internal/helm/shell_client.go) instead execs the helm binary (with a comment explaining the pivot to avoid dependency conflicts). The pivot is reasonable, but the spec must be updated to match — otherwise the desired-state doc contradicts the code, and the runtime now has a new hard dependency on a helm binary in the image + HELM_BINARY/PATH resolution that the spec never describes.

4. Already-deployed releases are never upgraded (create-or-skip)Reconciliation convention

  • internal/gateway/helm_deploy.go:74 skips when status == "deployed", so image/OIDC/route/config changes to a running gateway never converge. The spec acknowledges "Gateway upgrades are not handled at this time," but this is a functional regression from the previous SSA path, which did re-apply on spec changes, and it violates "reconcile, don't create-or-skip." Relatedly, internal/helm/shell_client.go:152 uses --reuse-values together with --values on the retry path, so keys removed from the desired set persist across retries. Please call this limitation out explicitly in the PR body and file a follow-up, and coordinate with PR #151 (see Cross-PR section).

5. Out-of-band route teardown will drift Helm stateArchitecture

  • internal/reconciler/health.go:439 self-documents that DeleteGatewayAPIResources deletes chart-managed routing resources outside Helm, "may cause Helm state drift." With routing now owned by the chart, the health loop and Helm will fight over these resources (Helm still believes them present; a later reconcile may recreate). This needs a real plan (e.g., helm upgrade with routing disabled) rather than a TODO before it lands.

Minor

6. splitImageRef mishandles digest referencesinternal/helm/values.go:247 splits on the last :; an image like repo@sha256:abcd yields repo=...@sha256, tag=abcd. Not hit by the current tag-based defaults, but will silently corrupt digest-pinned images. Guard for @.

7. Pointless indirectioninternal/gateway/helm_deploy.go:111 getEnvgetEnvHelperos.Getenv. Collapse to a single helper (or reuse an existing env helper) to reduce noise.

8. Lost documentationinternal/config/config.go dropped the detailed DatabaseProvider comment explaining the CNPG-vs-deployment default and no-silent-fallback behavior with no functional change. Keep the rationale.

9. Delegated pod SecurityContext is now unverifiable here — pod security context comes entirely from the upstream chart, and buildOpenShiftValues nils podSecurityContext.fsGroup/securityContext.runAsUser to defer to SCC. Please confirm the chart still yields runAsNonRoot: true, allowPrivilegeEscalation: false, and drop: ["ALL"] per security.spec.md, since we no longer set these in-tree.

Cross-PR coordination

I compared #194 against the other open PRs in openshift-online/hypershell. Open PRs at review time: #216, #214, #212, #211, #210, #209, #208, #207, #206, #201, #200, #194, #189, #188, #185, #182, #179, #151, #150, #148, #135, #109, #75, #73. The following have material (design/plan) conflicts, not mere text overlap:

  • #201 — "[HYPERSHELL-45] Update gateway and supervisor openshell images to Red Hat ones": Direct, competing design conflict over the canonical gateway/supervisor image source. #201 removes the in-code image defaults entirely ("now they are required to be set as env variables or inside the gatewayconfig resource") and switches to Red Hat images; #194 does the opposite — it keeps and relies more heavily on in-code defaults (reconciler.go:1450 now backfills images.DefaultGatewayImage()) and points them at a personal fork (config.go:40). Both edit internal/gateway/config.go and scripts/kind/lib.sh, and #194 deletes internal/gateway/manifests.go which #201 modifies. Maintainer decision needed: pick the canonical image source (Red Hat vs upstream vs fork) and whether in-code defaults survive; then sequence these two PRs deliberately.

  • #151 — "gate gateway re-provisioning on desired-state convergence": Opposed goals on the same Handle path. #151 exists to make the reconciler re-apply on spec changes (image/route/oidc/db) so drift stops being masked; #194 introduces a deploy path that explicitly skips already-deployed releases (helm_deploy.go:74) and states upgrades are out of scope. Both edit internal/reconciler/reconciler.go and skills/RECONCILE.md. Coordination needed: #151's convergence gate must drive a helm upgrade under #194's model, or the two designs will cancel out.

  • #216 — "fix(console): support OpenShift Route ingress": Conflicting ownership of ingress/Route creation and overlapping teardown. #194 removes control-plane gateway Route reconciliation and delegates GRPCRoute/Route to the chart, renames openshell-backend-caopenshell-gateway-backend-ca, and flags health.go route teardown as Helm-drift-prone; #216 adds control-plane-managed console Routes and edits the same health.go teardown path, reconciler.go, and reconciler_test.go. Coordination needed: agree on who owns Route objects (chart vs control plane) and reconcile the health.go teardown semantics.

  • #179 — "reconcile existing Keycloak clients on gated gateways" (lower priority): Overlapping restructure of internal/reconciler/reconciler.go and health.go around the phase gate / Keycloak reconcile ordering that #194 also rewrites. This is primarily a merge-ordering concern rather than a design clash, but whichever lands first will force a non-trivial rebase of the other.

No material conflict found with the remaining open PRs (#211/#150/#148 touch kind/image-build tooling and specs that overlap only textually with #194; #200/#185 are reconciliation-contract specs that #194 does not contradict; the rest are UI/console/deps unrelated to this change).


Findings Summary (ordered by severity, highest first):

  1. [Blocker] Image build clones a personal fork's mutable branch and pins a personal quay image; non-reproducible + inconsistent references - Security / Supply chain (Dockerfile L29, CHART_REPO/CHART_REF, config.go L40-41)
  2. [Major] Database-config resolution error swallowed; gateway deploys anyway and phase never reflects failure - Error handling (reconciler.go L1414)
  3. [Major] Spec mandates Helm Go SDK; code shells out to helm CLI - Spec consistency (helm-adoption spec, shell_client.go)
  4. [Major] Deployed releases never upgraded (create-or-skip) + --reuse-values on retry - Reconciliation (helm_deploy.go L74, shell_client.go L152)
  5. [Major] Out-of-band route teardown drifts Helm state (self-admitted TODO) - Architecture (health.go L439)
  6. [Minor] splitImageRef corrupts digest references - Correctness (values.go L247)
  7. [Minor] Pointless getEnv/getEnvHelper indirection - Style (helm_deploy.go L111)
  8. [Minor] Dropped DatabaseProvider rationale comment - Docs (config.go)
  9. [Minor] Pod SecurityContext fully delegated to external chart; verify it still meets security.spec.md - Security (values.go buildOpenShiftValues)

Convention Checklist:

Convention Result
No panic() in production code Pass
Errors wrapped with fmt.Errorf context Pass
Never silently swallow partial failures Fail (reconciler.go L1414)
Reconcile, don't create-or-skip Fail (helm_deploy.go L74)
Image references consistent across the stack Fail (config.go L40-41, Dockerfile, kind/lib.sh)
Reproducible builds / pinned dependencies Fail (Dockerfile L29 mutable branch clone)
Spec matches implementation Fail (Helm SDK vs CLI)
SecurityContext on pod specs Deferred to upstream chart (verify)
Status updated on error paths Fail (db-config path, reconciler.go L1414)
Conventional commit message Pass

Rollback: this is a self-contained control-plane change; reverting the merge commit restores the SSA manifest path. Happy to pair on the upstream-chart pinning once #2728 lands. Does this framing match your intent for the temporary fork references?

COPY charts/CHART_REPO charts/CHART_REF /tmp/chart-source/
RUN CHART_REPO=$(cat /tmp/chart-source/CHART_REPO) && \
CHART_REF=$(cat /tmp/chart-source/CHART_REF) && \
git clone --depth 1 --branch "${CHART_REF}" "${CHART_REPO}" /tmp/openshell && \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Blocker] Non-reproducible build from a mutable personal fork branch. git clone --depth 1 --branch "${CHART_REF}" where CHART_REF=feat/backend-tls-and-rbac-toggle (a branch, not an immutable tag/SHA) on https://github.com/bsquizz/OpenShell.git means the control-plane image content can change or vanish with no code change here, and CI can break at any time. Pin to the canonical upstream repo at an immutable tag/SHA (or explicitly gate this PR behind upstream NVIDIA/OpenShell #2728) before merging to main.

Comment thread charts/CHART_REPO
@@ -0,0 +1 @@
https://github.com/bsquizz/OpenShell.git

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Blocker] Points the chart source at a personal fork (github.com/bsquizz/OpenShell). Combined with the mutable branch in CHART_REF, this makes production builds depend on personal, mutable infrastructure. Needs a maintainer decision on the canonical chart source + immutable pinning.


const defaultGatewayImage = "ghcr.io/nvidia/openshell/gateway:0.0.109"
const defaultSupervisorImage = "ghcr.io/nvidia/openshell/supervisor:0.0.109"
const defaultGatewayImage = "quay.io/bsquizza/openshell-gateway:16112bc"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Blocker/Major] Personal registry + mixed provenance. defaultGatewayImage now points at quay.io/bsquizza/openshell-gateway:16112bc (personal quay) while defaultSupervisorImage (L41) stays on ghcr.io/nvidia/openshell/supervisor:16112bc. CLAUDE.md requires image references to be consistent across the stack. Also note PR #201 proposes removing these in-code defaults entirely in favor of Red Hat images — coordinate on the canonical source before merge.

if resolveErr != nil {
reconcileErr = fmt.Errorf("resolve database config for gateway %s: %w", gw.Name, resolveErr)
return reconcileErr
log.Printf("WARN gateway %s: skipping database reconciliation: %v", event.ResourceID, resolveErr)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Major] Swallowed partial failure. This changed from return reconcileErr (which marked the gateway Failed) to a warning-and-continue. The reconcile now proceeds to the Helm install referencing openshell-gateway-db-credentials, which may not exist, and the gateway phase never reflects the failure. This violates the "never silently swallow partial failures" rule and "status updated on error paths." Restore fail-closed behavior or document why proceeding is safe.

if err := helmClient.Upgrade(ctx, nsConfig.Name, values); err != nil {
return fmt.Errorf("helm upgrade: %w", err)
}
} else if status.Status == "deployed" {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Major] Create-or-skip anti-pattern. When the release is already deployed, this logs and skips, so image/OIDC/route/config changes to a running gateway never converge. This is a regression from the SSA path (which re-applied) and conflicts with the "reconcile, don't create-or-skip" convention. It also directly overlaps PR #151's goal of converging on desired-state changes. If out of scope for now, call it out explicitly in the PR body and file a follow-up.

args := []string{
"upgrade", ReleaseName, c.ChartPath,
"--namespace", namespace,
"--reuse-values",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Major] --reuse-values on the retry path. Upgrade passes both --reuse-values and --values <file>; keys removed from the desired set persist across retries, and on a retried failed install the prior (bad) values are merged back in. Consider --reset-values + full desired values so the release always reflects the computed values map.

}
}
// NOTE: With Helm adoption, routing resources are managed by the chart.
// This manual cleanup is retained for partial route teardown but may cause

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Major] Self-admitted Helm state drift. Deleting chart-managed routing resources out-of-band leaves Helm believing they still exist; a later reconcile can recreate them. With routing now owned by the chart this needs a concrete plan (e.g. helm upgrade with routing disabled) rather than a TODO. Also interacts with PR #216, which adds control-plane-managed console Routes on this same teardown path.

func splitImageRef(image string) (repo, tag string) {
lastSlash := strings.LastIndex(image, "/")
lastColon := strings.LastIndex(image, ":")
if lastColon <= lastSlash {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Minor] Digest refs mis-split. image@sha256:... yields repo=...@sha256, tag=<hash> because the split is on the last :. Guard for @ (digest) before splitting on :. Not hit by current tag defaults, but will silently corrupt digest-pinned images.

}

// getEnv retrieves an environment variable with a fallback default.
func getEnv(key, fallback string) string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Minor] Pointless indirection. getEnv -> getEnvHelper -> os.Getenv. Collapse into one helper (or reuse an existing env helper in the package).

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

Labels

amber/approved The Amber review agent has approved this PR. amber/self-review This PR was reviewed by the Amber review agent by one of the contributors to the PR. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants