Skip to content

feat: add HCP proxy CreateRequest to create HostedClusters from the hub - #760

Merged
openshift-merge-bot[bot] merged 2 commits into
stolostron:mainfrom
yiraeChristineKim:ACM-37268
Jul 22, 2026
Merged

feat: add HCP proxy CreateRequest to create HostedClusters from the hub#760
openshift-merge-bot[bot] merged 2 commits into
stolostron:mainfrom
yiraeChristineKim:ACM-37268

Conversation

@yiraeChristineKim

@yiraeChristineKim yiraeChristineKim commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add hub-side HCP proxy CreateRequest so platform engineers can create a HostedCluster (plus NodePools and Secrets) on a managed hosting cluster using hub credentials, without a per-cluster kubeconfig (ACM-37268).
  • Creation order: Namespace → Secrets → HostedCluster → NodePool(s), forwarded via cluster-proxy, with managedcluster:admin auth, ManagedCluster health checks, duplicate-name (409) handling, and hcp.ocm.io/created-via=hcp-from-hub labels.
  • Includes unit/e2e coverage for the proxy create path, plus hub manager stability fixes (disable WatchListClient; scope AddOnDeploymentConfig informer).

Test plan

  • Unit tests for handleCreate (auth failure, ManagedCluster unhealthy, duplicate create) pass
  • e2e: create AWS HostedCluster via proxy and verify it reaches Available
  • Verify 403 when caller lacks managedcluster:admin on the target hosting cluster
  • Verify created resources carry hcp.ocm.io/created-via and hcp.ocm.io/hostedcluster labels

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added an HCP Proxy API for managing HostedClusters and NodePools from the hub.
    • Added secure request routing, identity propagation, health checks, discovery endpoints, and permission validation.
    • Added local Kind-based HCP Proxy end-to-end testing workflows.
  • Documentation

    • Added guides for using hcp from-hub and developing/testing the HCP Proxy locally.
    • Updated README navigation and quickstart guidance.
  • Bug Fixes

    • Improved TLS profile handling and restart behavior when cluster security settings change.
    • Improved local OCM and cluster-proxy setup reliability.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a hub-side HCP proxy API for HostedCluster and NodePool management, central TLS-profile handling, local Kind E2E automation, deployment manifests, developer tooling, documentation, and supporting dependency updates.

Changes

HCP proxy

Layer / File(s) Summary
Proxy server and request security
pkg/manager/hcp_proxy.go, pkg/manager/hcp_proxy_test.go
Adds HTTPS serving, discovery, URL resolution, TLS certificate loading or generation, caller identity handling, spoke availability checks, hub permission checks, impersonation, and SSRF-safe path validation.
Bundle operations and search
pkg/manager/hcp_proxy.go, pkg/manager/hcp_proxy_test.go
Adds create, read, list, full-bundle replacement, delete, secret upsert, NodePool filtering, and ACM Search integration with corresponding tests.
TLS lifecycle and deployment wiring
pkg/manager/manager.go, test/e2e/addon-manager-deployment.yaml, .cursor/rules/*, CLAUDE.md, go.mod
Adds TLS profile watcher shutdown handling, WatchListClient disabling, proxy deployment resources, TLS compliance guidance, cross-repository template synchronization guidance, and Kubernetes dependency updates.
Kind E2E environment and validation
Makefile, .github/workflows/e2e-hcp-proxy.yaml, hack/*, Dockerfile.e2e, test/e2e/*, .dockerignore
Adds Kind setup and cleanup, OCM and cluster-proxy installation, image building, addon-manager deployment, readiness checks, CI execution, and direct/APIService-routed HCP proxy tests.
Developer workflows and API documentation
.vscode/launch.json, docs/management/*, README.md, docs/README.md, quickstart/README.md
Adds debug configurations, local proxy instructions, hcp from-hub API and CLI documentation, and navigation links.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Hub API
  participant HCP Proxy
  participant Cluster Proxy
  participant Spoke API
  Client->>Hub API: Call hcp.ocm.io/v1alpha1
  Hub API->>HCP Proxy: Route APIService request
  HCP Proxy->>Cluster Proxy: Forward validated impersonated request
  Cluster Proxy->>Spoke API: Access HostedCluster or NodePool
  Spoke API-->>HCP Proxy: Return resource result
  HCP Proxy-->>Client: Return API response
Loading

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 3 warnings)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error New HCP proxy logs raw request paths and HostedCluster/namespace/spoke names, which can reveal customer identifiers. Redact paths and object identifiers in logs; emit generic messages or hashed IDs only.
Description check ⚠️ Warning The description omits required template sections for why, issue reference, and test success output, so it doesn't match the repo format. Rewrite it to match the template with Description of the change(s), Why do we need this PR, Issue reference, and Test API/Unit - Success including test output.
Docstring Coverage ⚠️ Warning Docstring coverage is 44.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning Many assertions still lack contextual failure messages (17 bare Gomega checks in e2e and many require.NoError/assert.NoError in unit tests). Add short, behavior-specific messages to bare assertions, especially in test/e2e/hcp_proxy_test.go, e2e_suite_test.go, and pkg/manager/hcp_proxy_test.go; avoid direct endpoint dials in unit tests.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately captures the main hub-side HCP proxy CreateRequest change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All Ginkgo titles are fixed literals; no title uses generated names, dates, IPs, or fmt.Sprintf, and e2e_suite_test.go has no Ginkgo DSL titles.
No-Weak-Crypto ✅ Passed Scanned the changed Go files: no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB or custom crypto; TLS/cert handling uses OpenShift profile APIs and library-go helpers.
Container-Privileges ✅ Passed No added manifest sets privileged/hostPID/hostNetwork/hostIPC/SYS_ADMIN; the new pod spec disables privilege escalation, drops ALL caps, and runs as UID 65532.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 17

🧹 Nitpick comments (2)
.work/jira/solve/spec-OCPBUGS-89352.md (1)

37-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Validate the shell script directly.

make fmt and make vet do not validate the Bash syntax or confirm that the obsolete binary path was removed. Add bash -n, ShellCheck if used by the repository, and a targeted search or canary execution to the verification steps.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.work/jira/solve/spec-OCPBUGS-89352.md around lines 37 - 40, Update the
Verification section to validate the shell script directly: add bash -n, run the
repository’s ShellCheck command if applicable, and include a targeted search or
canary execution confirming the obsolete binary path is absent.
README.md (1)

68-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Specify a language for fenced code blocks.

Several fenced code blocks are missing a language identifier, which triggers markdownlint-cli2 (MD040) warnings.

  • README.md#L68-L68: append text or bash to the opening fence (e.g., ````text`).
  • docs/management/from-hub-cli.md#L11-L11: append text to the opening fence.
  • docs/management/from-hub-cli.md#L53-L53: append text to the opening fence.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 68, Specify a language on each affected fenced code block:
append text or bash to the opening fence in README.md at lines 68-68, and append
text to the opening fences in docs/management/from-hub-cli.md at lines 11-11 and
53-53. No other content changes are needed.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.cursor/rules/backplane-operator-sync.mdc:
- Line 17: Replace the developer-specific absolute checkout path in the rule
file with a repository-relative or clearly generic placeholder reference to the
backplane-operator repository, removing the embedded username and local
filesystem location.

In @.cursor/rules/tls-compliance.mdc:
- Around line 40-43: Update the NewTLSConfigFromProfile example to bind its
second return value as unsupported rather than err, then handle the
len(unsupported) > 0 case with the appropriate failure behavior. Remove the
invalid error-based check so the documented signature matches the actual API and
compiles.

In @.github/workflows/e2e-hcp-proxy.yaml:
- Around line 24-28: Add a job-level permissions block to e2e-hcp-proxy with all
GITHUB_TOKEN permissions denied by default and only contents read access
enabled, since the HCP Proxy E2E job only needs to read repository contents.
- Around line 30-36: Update the workflow steps using actions/checkout and
actions/setup-go to reference immutable full commit SHAs instead of version
tags, preserving their current action versions. Add persist-credentials: false
to the checkout step while leaving the existing setup-go configuration
unchanged.
- Around line 42-44: Remove the KUBECONFIG override from the e2e step, or set it
using the runner’s $HOME shell variable within the run command rather than
env.HOME. Preserve the existing make test-e2e-hcp-proxy invocation and
KIND_CLUSTER_NAME argument.

In `@go.mod`:
- Line 35: Update the k8s.io/kube-aggregator dependency in go.mod from v0.34.2
to v0.35.2, matching the versions used by the other k8s.io modules. Then rerun
make vendor to refresh vendored dependencies.

In `@Makefile`:
- Line 249: Update the namespace creation command to use $(KUBECTL) for the
piped apply invocation as well as the create invocation, preserving support for
KUBECTL overrides consistently with the surrounding target.
- Around line 215-218: Update the e2e-hcp-proxy-full target so kind-delete
always runs after test-e2e-hcp-proxy, including when the test fails. Capture the
test command’s exit status, perform cleanup unconditionally, then return the
original test status.

In `@pkg/manager/hcp_proxy_test.go`:
- Line 1: Prepend the repository-standard Apache 2.0 copyright header,
attributing “Red Hat, Inc.” or “stolostron contributors,” to
pkg/manager/hcp_proxy_test.go at lines 1-1 and test/e2e/hcp_proxy_test.go at
lines 1-1, before each package declaration.

In `@pkg/manager/hcp_proxy.go`:
- Around line 839-843: Update the NodePool creation loop around createOnSpoke so
failed creations are not reported as created: on error, skip appending that
NodePool to createdNodePools, or accumulate the failure and return an
appropriate partial-failure response instead of the current success response.
Preserve logging and successful NodePool reporting.
- Around line 604-619: Update the probe error handling in the hub
permission-check flow around hubDynClient and
hubDynClient.Resource(gvr).Resource errors: retain the non-fatal return only for
the specific unregistered-API NotFound case, and propagate every other probe
error instead of logging and returning nil, so authorization fails closed.
- Line 1: Add the required Apache 2.0 copyright header at the top of the new
hcp_proxy.go file, before the package declaration, including either “Red Hat,
Inc.” or “stolostron contributors” as required by the repository path
instructions.
- Around line 878-884: The NodePool deletion loop in the surrounding manager
flow leaks HTTP response bodies and ignores request-construction errors. Update
the code around hcpClient.Do(req) to handle http.NewRequestWithContext errors
before sending, and close every successful response body after the delete
request; preserve the existing iteration and deletion behavior.
- Around line 152-169: Update the TLS configuration in the HCP proxy server
setup to require and verify client certificates before accepting X-Remote-*
identity headers. Wire tlsCfg.ClientAuth and tlsCfg.ClientCAs to the
requestheader-client-ca bundle using the existing certificate-loading
configuration, while preserving the current server certificate and handler
setup.

In `@test/e2e/addon-manager-deployment.yaml`:
- Around line 76-81: Update the container definition’s securityContext to
include runAsNonRoot: true, add CPU and memory resource limits, and configure
liveness and readiness probes for the existing proxy endpoints on port 8443:
/healthz and /readyz respectively. Keep the existing privilege-escalation and
read-only filesystem settings.

In `@test/e2e/hcp_proxy_test.go`:
- Around line 299-317: Replace the tautological matcher in the Eventually call
within the hostedclusters proxy test with a callback that performs the REST
request and asserts statusCode is not http.StatusOK, returning the assertion
result for retry evaluation. Remove the separate post-Eventually status
assertion so polling continues until the proxy responds with a non-200 status.
- Around line 209-213: Update the discovery response assertions in the
handleDiscovery test to expect two resources, then verify both entries include
hostedclusters and hostedclusters/resources. Preserve the existing
hostedclusters assertion and add an assertion for the published subresource.

---

Nitpick comments:
In @.work/jira/solve/spec-OCPBUGS-89352.md:
- Around line 37-40: Update the Verification section to validate the shell
script directly: add bash -n, run the repository’s ShellCheck command if
applicable, and include a targeted search or canary execution confirming the
obsolete binary path is absent.

In `@README.md`:
- Line 68: Specify a language on each affected fenced code block: append text or
bash to the opening fence in README.md at lines 68-68, and append text to the
opening fences in docs/management/from-hub-cli.md at lines 11-11 and 53-53. No
other content changes are needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: stolostron/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f1a66eb0-a500-4764-a6c1-eeb61f283581

📥 Commits

Reviewing files that changed from the base of the PR and between 1895b4e and fa3907350df1807b3d19eb083177da2740afdc6f.

⛔ Files ignored due to path filters (1)
  • .cursor/debug-8d39ff.log is excluded by !**/*.log
📒 Files selected for processing (20)
  • .cursor/rules/backplane-operator-sync.mdc
  • .cursor/rules/tls-compliance.mdc
  • .github/workflows/e2e-hcp-proxy.yaml
  • .vscode/launch.json
  • .work/jira/solve/spec-OCPBUGS-89352.md
  • CLAUDE.md
  • Makefile
  • README.md
  • docs/README.md
  • docs/management/from-hub-cli.md
  • go.mod
  • pkg/manager/discovery_config_controller.go
  • pkg/manager/hcp_proxy.go
  • pkg/manager/hcp_proxy_test.go
  • pkg/manager/manager.go
  • pkg/manager/manifests/hcp-proxy/apiservice.yaml
  • pkg/manager/manifests/hcp-proxy/rbac.yaml
  • pkg/manager/manifests/hcp-proxy/service.yaml
  • test/e2e/addon-manager-deployment.yaml
  • test/e2e/hcp_proxy_test.go

Comment thread .cursor/rules/backplane-operator-sync.mdc Outdated
Comment thread .cursor/rules/tls-compliance.mdc Outdated
Comment thread .github/workflows/e2e-hcp-proxy.yaml
Comment thread .github/workflows/e2e-hcp-proxy.yaml
Comment thread .github/workflows/e2e-hcp-proxy.yaml Outdated
Comment thread pkg/manager/hcp_proxy.go Outdated
Comment thread pkg/manager/hcp_proxy.go Outdated
Comment thread test/e2e/addon-manager-deployment.yaml
Comment thread test/e2e/hcp_proxy_test.go
Comment thread test/e2e/hcp_proxy_test.go Outdated
@yiraeChristineKim
yiraeChristineKim force-pushed the ACM-37268 branch 2 times, most recently from 03546d7 to 3f623f9 Compare July 20, 2026 17:56

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
pkg/manager/hcp_proxy.go (1)

146-154: 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

X-Remote-* identity is still trusted without client-cert verification.

The server enables only the serving cert; ClientAuth/ClientCAs are not wired to the requestheader-client-ca bundle. Because the Service fronts this backend on 443 → 9443, any in-cluster workload that can reach the ClusterIP can set X-Remote-User/X-Remote-Group (consumed by whoIsTheCaller at Line 500) and be impersonated for both the hub permission check and spoke writes. The added comment documents the risk but does not close it — the aggregation guarantee only holds for traffic that actually transits kube-apiserver, not for direct in-cluster connections.

Gate X-Remote-* on verified client certs against the requestheader CA (or otherwise ensure the port is only reachable via the aggregator).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/manager/hcp_proxy.go` around lines 146 - 154, Update the TLS
configuration in the hcp proxy setup around tlsConfigFn and tlsCfg to require
verified client certificates using the requestheader-client-ca bundle, wiring
ClientAuth and ClientCAs appropriately before serving requests. Ensure
whoIsTheCaller only trusts X-Remote-* identity headers after this verification,
or otherwise restrict direct backend access so the service cannot be reached
without aggregator authentication.
🧹 Nitpick comments (3)
pkg/manager/hcp_proxy_test.go (1)

1494-1503: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fixed time.Sleep(200ms) can flake under CI load.

StartHCPProxy binds asynchronously and the test relies on a hard-coded sleep before cancelling. On a slow runner the server may not be listening yet, making the graceful-shutdown assertion nondeterministic. Prefer polling readiness (e.g. dial the bound address until it accepts) before cancel, or expose the listener address so the test can wait deterministically.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/manager/hcp_proxy_test.go` around lines 1494 - 1503, Replace the fixed
time.Sleep in the StartHCPProxy shutdown test with deterministic readiness
polling: wait until the TLS server has successfully bound and accepts a
connection before calling cancel. Use the server’s bound address or an exposed
listener readiness signal, retain the existing timeout failure behavior, and
then perform the current errCh graceful-shutdown assertion.
Makefile (1)

156-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Harden ensure-kind download to match ensure-clusteradm.

Two robustness gaps versus ensure-clusteradm: (1) $(GOBIN) is not created, so curl -sSLo "$(GOBIN)/kind" fails if the directory doesn't exist; (2) curl lacks -f, so an HTTP error response is written to the kind binary and then chmod +x'd, causing confusing downstream failures.

🔧 Proposed fix
 	`@if` ! command -v kind >/dev/null 2>&1 && [ ! -f "$(GOBIN)/kind" ]; then \
 	  OS=$$(uname -s | tr '[:upper:]' '[:lower:]'); \
 	  ARCH=$$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/'); \
 	  echo "Installing kind $(KIND_VERSION) for $$OS/$$ARCH into $(GOBIN)..."; \
-	  curl -sSLo "$(GOBIN)/kind" \
+	  mkdir -p "$(GOBIN)"; \
+	  curl -fsSLo "$(GOBIN)/kind" \
 	    "https://kind.sigs.k8s.io/dl/$(KIND_VERSION)/kind-$$OS-$$ARCH"; \
 	  chmod +x "$(GOBIN)/kind"; \
 	fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 156 - 166, Harden the ensure-kind target by creating
$(GOBIN) before downloading and adding curl’s fail-on-HTTP-error option to the
kind download command. Keep the existing OS/architecture detection,
installation, chmod, and version verification flow unchanged.
test/e2e/hcp_proxy_test.go (1)

284-297: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused cfg.

cfg from util.NewKubeConfig() is never used — the discovery poll goes through kubeClient.Discovery(), and the only reference is the _ = cfg no-op inside the closure. Drop the fetch and the discard.

♻️ Proposed cleanup
 		ginkgo.By("Waiting for hcp.ocm.io to appear in server API groups")
-		cfg, err := util.NewKubeConfig()
-		gomega.Expect(err).ToNot(gomega.HaveOccurred())
-
 		gomega.Eventually(func() bool {
 			groups, _, err := kubeClient.Discovery().ServerGroupsAndResources()
 			if err != nil {
 				return false
 			}
 			for _, g := range groups {
 				if g.Name == hcpProxyAPIGroup {
 					return true
 				}
 			}
-			_ = cfg
 			return false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/hcp_proxy_test.go` around lines 284 - 297, Remove the unused cfg
initialization and discard from the test setup around
kubeClient.Discovery().ServerGroupsAndResources(); delete the
util.NewKubeConfig() call and the _ = cfg statement, preserving the existing
discovery polling behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/management/from-hub-cli.md`:
- Around line 149-151: Update the fenced CLI example containing `hcp from-hub
create <platform> [flags]` to specify the Bash language identifier, using a bash
fence while preserving the command unchanged.

In `@pkg/manager/hcp_proxy.go`:
- Around line 1009-1049: Handle every http.NewRequestWithContext error in the
affected handler, including the namespace, HostedCluster, and NodePool request
construction near the existing request flow. Check each construction result
before calling hcpClient.Do, return an appropriate client error response, and
wrap the underlying error with fmt.Errorf using contextual messages rather than
discarding it.

---

Duplicate comments:
In `@pkg/manager/hcp_proxy.go`:
- Around line 146-154: Update the TLS configuration in the hcp proxy setup
around tlsConfigFn and tlsCfg to require verified client certificates using the
requestheader-client-ca bundle, wiring ClientAuth and ClientCAs appropriately
before serving requests. Ensure whoIsTheCaller only trusts X-Remote-* identity
headers after this verification, or otherwise restrict direct backend access so
the service cannot be reached without aggregator authentication.

---

Nitpick comments:
In `@Makefile`:
- Around line 156-166: Harden the ensure-kind target by creating $(GOBIN) before
downloading and adding curl’s fail-on-HTTP-error option to the kind download
command. Keep the existing OS/architecture detection, installation, chmod, and
version verification flow unchanged.

In `@pkg/manager/hcp_proxy_test.go`:
- Around line 1494-1503: Replace the fixed time.Sleep in the StartHCPProxy
shutdown test with deterministic readiness polling: wait until the TLS server
has successfully bound and accepts a connection before calling cancel. Use the
server’s bound address or an exposed listener readiness signal, retain the
existing timeout failure behavior, and then perform the current errCh
graceful-shutdown assertion.

In `@test/e2e/hcp_proxy_test.go`:
- Around line 284-297: Remove the unused cfg initialization and discard from the
test setup around kubeClient.Discovery().ServerGroupsAndResources(); delete the
util.NewKubeConfig() call and the _ = cfg statement, preserving the existing
discovery polling behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: stolostron/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a5dbdf4e-3d3f-4d5f-ae22-651742874f29

📥 Commits

Reviewing files that changed from the base of the PR and between fa3907350df1807b3d19eb083177da2740afdc6f and 26174dc801ceb8787b7d1d388ea8fed8740b147a.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (18)
  • .cursor/rules/backplane-operator-sync.mdc
  • .cursor/rules/tls-compliance.mdc
  • .github/workflows/e2e-hcp-proxy.yaml
  • .vscode/launch.json
  • CLAUDE.md
  • Dockerfile
  • Makefile
  • README.md
  • docs/README.md
  • docs/management/from-hub-cli.md
  • go.mod
  • pkg/agent/agent.go
  • pkg/manager/discovery_config_controller.go
  • pkg/manager/hcp_proxy.go
  • pkg/manager/hcp_proxy_test.go
  • pkg/manager/manager.go
  • test/e2e/addon-manager-deployment.yaml
  • test/e2e/hcp_proxy_test.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • .vscode/launch.json
  • .cursor/rules/backplane-operator-sync.mdc
  • docs/README.md
  • .github/workflows/e2e-hcp-proxy.yaml
  • CLAUDE.md
  • README.md
  • pkg/manager/discovery_config_controller.go
  • pkg/manager/manager.go

Comment on lines +149 to +151
```
hcp from-hub create <platform> [flags]
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Specify the fenced block language.

The code fence at Line 149 lacks a language identifier, triggering Markdownlint MD040. Use ```bash for this CLI example.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 149-149: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/management/from-hub-cli.md` around lines 149 - 151, Update the fenced
CLI example containing `hcp from-hub create <platform> [flags]` to specify the
Bash language identifier, using a bash fence while preserving the command
unchanged.

Source: Linters/SAST tools

Comment thread pkg/manager/hcp_proxy.go Outdated
@yiraeChristineKim
yiraeChristineKim force-pushed the ACM-37268 branch 2 times, most recently from a38e0f8 to f403dc2 Compare July 20, 2026 19:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
pkg/manager/manager.go (1)

263-274: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

fetchTLSProfileOrDefault never returns a non-nil error.

The error return is dead — the fetch failure is already handled via the Intermediate fallback. Consider dropping the return value (and the caller's if err != nil at Lines 156-159) so the signature reflects that this cannot fail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/manager/manager.go` around lines 263 - 274, Remove the unused error
return from fetchTLSProfileOrDefault, since fetch failures already fall back to
the Intermediate TLS profile. Update its callers, including the handling around
the caller at Lines 156-159, to use the returned TLSProfileSpec directly and
eliminate the dead if err != nil branch while preserving the existing fallback
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/management/hcp-proxy-local-dev.md`:
- Around line 126-136: Update the “Identity header reference” documentation to
state that the hub permission gate is enforced on ACM/MCE hubs but skipped when
the clusterview API is unavailable, including kind/non-ACM local development.
Preserve the existing permission-check description for ACM/MCE environments.

In `@hack/install_ocm.sh`:
- Around line 34-40: Suppress shell tracing around the credential-parsing and
error-reporting logic in install_ocm.sh: disable xtrace before the hub_token
assignment and related join.sh parsing, validation, and cat join.sh output, then
restore the prior tracing state afterward. Ensure neither the hub token nor
join.sh contents appear in CI logs while preserving the existing validation and
exit behavior.

In `@pkg/manager/manager.go`:
- Around line 180-187: Update the synchronous EnableHypershiftCLIDownload call
in the manager startup flow to pass managerCtx instead of the parent ctx, so its
retry loop observes cancelManager() and stops during a graceful restart.
Preserve the existing error logging and subsequent managerCtx wait behavior.

---

Nitpick comments:
In `@pkg/manager/manager.go`:
- Around line 263-274: Remove the unused error return from
fetchTLSProfileOrDefault, since fetch failures already fall back to the
Intermediate TLS profile. Update its callers, including the handling around the
caller at Lines 156-159, to use the returned TLSProfileSpec directly and
eliminate the dead if err != nil branch while preserving the existing fallback
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: stolostron/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2f3ca871-f0c2-486f-a229-84e15c95e665

📥 Commits

Reviewing files that changed from the base of the PR and between 26174dc801ceb8787b7d1d388ea8fed8740b147a and 24cff63601385f9e01d7ab94902e22e0018df1d9.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (20)
  • .cursor/rules/backplane-operator-sync.mdc
  • .cursor/rules/tls-compliance.mdc
  • .github/workflows/e2e-hcp-proxy.yaml
  • .vscode/launch.json
  • CLAUDE.md
  • Dockerfile
  • Makefile
  • README.md
  • docs/README.md
  • docs/management/from-hub-cli.md
  • docs/management/hcp-proxy-local-dev.md
  • go.mod
  • hack/install_ocm.sh
  • pkg/agent/agent.go
  • pkg/manager/hcp_proxy.go
  • pkg/manager/hcp_proxy_test.go
  • pkg/manager/manager.go
  • quickstart/README.md
  • test/e2e/addon-manager-deployment.yaml
  • test/e2e/hcp_proxy_test.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • Dockerfile
  • .vscode/launch.json
  • pkg/agent/agent.go
  • .cursor/rules/backplane-operator-sync.mdc
  • go.mod
  • .github/workflows/e2e-hcp-proxy.yaml
  • .cursor/rules/tls-compliance.mdc
  • CLAUDE.md
  • pkg/manager/hcp_proxy_test.go

Comment on lines +126 to +136
## 4. Identity header reference

| Header | Example value | Notes |
|--------|--------------|-------|
| `X-Remote-User` | `kube:admin` | Must match a user with `managedcluster:admin` binding for the `hostingCluster` |
| `X-Remote-Group` | `system:cluster-admins` | One or more groups; used for spoke impersonation |

The proxy enforces two permission gates:

1. **Hub gate** — `GET clusterview.open-cluster-management.io/v1alpha1/userpermissions/managedcluster:admin`
under the caller's identity; request is denied if the target spoke is not in the bindings.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Document the kind/non-ACM permission-check exception.

The proxy skips the hub permission check when the clusterview API is unavailable, but this section says the caller must always have a managedcluster:admin binding and that the hub gate always denies otherwise. Clarify that this gate is enforced on ACM/MCE hubs and skipped for kind/non-ACM local development.

Suggested wording
-The proxy enforces two permission gates:
+On ACM/MCE hubs, the proxy enforces two permission gates. On kind or other
+non-ACM hubs where the clusterview API is unavailable, the hub gate is skipped:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## 4. Identity header reference
| Header | Example value | Notes |
|--------|--------------|-------|
| `X-Remote-User` | `kube:admin` | Must match a user with `managedcluster:admin` binding for the `hostingCluster` |
| `X-Remote-Group` | `system:cluster-admins` | One or more groups; used for spoke impersonation |
The proxy enforces two permission gates:
1. **Hub gate**`GET clusterview.open-cluster-management.io/v1alpha1/userpermissions/managedcluster:admin`
under the caller's identity; request is denied if the target spoke is not in the bindings.
## 4. Identity header reference
| Header | Example value | Notes |
|--------|--------------|-------|
| `X-Remote-User` | `kube:admin` | Must match a user with `managedcluster:admin` binding for the `hostingCluster` |
| `X-Remote-Group` | `system:cluster-admins` | One or more groups; used for spoke impersonation |
On ACM/MCE hubs, the proxy enforces two permission gates. On kind or other
non-ACM hubs where the clusterview API is unavailable, the hub gate is skipped:
1. **Hub gate**`GET clusterview.open-cluster-management.io/v1alpha1/userpermissions/managedcluster:admin`
under the caller's identity; request is denied if the target spoke is not in the bindings.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/management/hcp-proxy-local-dev.md` around lines 126 - 136, Update the
“Identity header reference” documentation to state that the hub permission gate
is enforced on ACM/MCE hubs but skipped when the clusterview API is unavailable,
including kind/non-ACM local development. Preserve the existing permission-check
description for ACM/MCE environments.

Comment thread hack/install_ocm.sh
Comment on lines +34 to +40
hub_apiserver=$(grep -oE -- '--hub-apiserver[[:space:]]+[^[:space:]]+' join.sh | awk '{print $2}' | head -1)
if [[ -z "${hub_token}" || -z "${hub_apiserver}" ]]; then
echo "ERROR: failed to parse hub token/apiserver from join.sh" >&2
cat join.sh >&2
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Hub join token is leaked into CI logs by set -xv.

With xtrace enabled globally (Line 9), the hub_token=$(...) assignment and cat join.sh echo the bearer token to the job output. Suppress tracing around credential handling.

Proposed fix
+{ set +x; } 2>/dev/null
 hub_token=$(grep -oE -- '--hub-token[[:space:]]+[^[:space:]]+' join.sh | awk '{print $2}' | head -1)
 hub_apiserver=$(grep -oE -- '--hub-apiserver[[:space:]]+[^[:space:]]+' join.sh | awk '{print $2}' | head -1)
 if [[ -z "${hub_token}" || -z "${hub_apiserver}" ]]; then
   echo "ERROR: failed to parse hub token/apiserver from join.sh" >&2
-  cat join.sh >&2
   exit 1
 fi
+set -x
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
hub_token=$(grep -oE -- '--hub-token[[:space:]]+[^[:space:]]+' join.sh | awk '{print $2}' | head -1)
hub_apiserver=$(grep -oE -- '--hub-apiserver[[:space:]]+[^[:space:]]+' join.sh | awk '{print $2}' | head -1)
if [[ -z "${hub_token}" || -z "${hub_apiserver}" ]]; then
echo "ERROR: failed to parse hub token/apiserver from join.sh" >&2
cat join.sh >&2
exit 1
fi
{ set +x; } 2>/dev/null
hub_token=$(grep -oE -- '--hub-token[[:space:]]+[^[:space:]]+' join.sh | awk '{print $2}' | head -1)
hub_apiserver=$(grep -oE -- '--hub-apiserver[[:space:]]+[^[:space:]]+' join.sh | awk '{print $2}' | head -1)
if [[ -z "${hub_token}" || -z "${hub_apiserver}" ]]; then
echo "ERROR: failed to parse hub token/apiserver from join.sh" >&2
exit 1
fi
set -x
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/install_ocm.sh` around lines 34 - 40, Suppress shell tracing around the
credential-parsing and error-reporting logic in install_ocm.sh: disable xtrace
before the hub_token assignment and related join.sh parsing, validation, and cat
join.sh output, then restore the prior tracing state afterward. Ensure neither
the hub token nor join.sh contents appear in CI logs while preserving the
existing validation and exit behavior.

Comment thread pkg/manager/manager.go
@yiraeChristineKim
yiraeChristineKim force-pushed the ACM-37268 branch 2 times, most recently from 9cd28dc to cefed1a Compare July 21, 2026 04:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 81-84: Update the documented TLS profile fallback around
FetchAPIServerTLSProfile and GetTLSProfileSpec so it never discards the fallback
error. Handle the returned error explicitly, wrapping it with contextual
information and returning or propagating it, or reference a helper that
validates and returns the fallback profile.

In `@test/e2e/hcp_proxy_test.go`:
- Around line 94-153: Add descriptive failure messages to the Gomega Expect
assertions in the affected It blocks, covering request errors, response status
codes, response body content, JSON decoding, document kinds and names, resource
counts and names, and the missing-hostingCluster status. Keep each message
concise and explain the expected behavior and its relevance, matching the
message style used near the later assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: stolostron/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 54f1d763-e282-4c90-96b3-d7cfa773fd8c

📥 Commits

Reviewing files that changed from the base of the PR and between bd6f2bad3c34eccc543caaf3d19fa8f4f8f11cb4 and 84e55167644474b64a5d4116cc5804d20301f4c3.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (23)
  • .cursor/rules/backplane-operator-sync.mdc
  • .cursor/rules/tls-compliance.mdc
  • .dockerignore
  • .github/workflows/e2e-hcp-proxy.yaml
  • .vscode/launch.json
  • CLAUDE.md
  • Dockerfile.e2e
  • Makefile
  • README.md
  • docs/README.md
  • docs/management/from-hub-cli.md
  • docs/management/hcp-proxy-local-dev.md
  • go.mod
  • hack/install_cluster_proxy.sh
  • hack/install_ocm.sh
  • pkg/agent/agent.go
  • pkg/manager/hcp_proxy.go
  • pkg/manager/hcp_proxy_test.go
  • pkg/manager/manager.go
  • quickstart/README.md
  • test/e2e/addon-manager-deployment.yaml
  • test/e2e/e2e_suite_test.go
  • test/e2e/hcp_proxy_test.go
🚧 Files skipped from review as they are similar to previous changes (16)
  • .dockerignore
  • .vscode/launch.json
  • Dockerfile.e2e
  • quickstart/README.md
  • README.md
  • .github/workflows/e2e-hcp-proxy.yaml
  • docs/README.md
  • test/e2e/e2e_suite_test.go
  • .cursor/rules/tls-compliance.mdc
  • go.mod
  • pkg/agent/agent.go
  • hack/install_ocm.sh
  • docs/management/hcp-proxy-local-dev.md
  • pkg/manager/manager.go
  • .cursor/rules/backplane-operator-sync.mdc
  • pkg/manager/hcp_proxy_test.go

Comment thread CLAUDE.md
Comment on lines +81 to +84
profileSpec, err := tlspkg.FetchAPIServerTLSProfile(ctx, hubClient)
if err != nil { // fallback for kind/non-OpenShift clusters
profileSpec, _ = tlspkg.GetTLSProfileSpec(nil) // Intermediate (TLS 1.2+)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Do not document an ignored fallback error.

The example discards GetTLSProfileSpec’s error with _, which conflicts with the repository rule to never ignore Go error returns. Show explicit handling or delegate to a helper that validates and returns the fallback profile.

As per path instructions, Go code must never ignore error returns and should wrap errors with context.

Proposed documentation fix
 if err != nil {  // fallback for kind/non-OpenShift clusters
-    profileSpec, _ = tlspkg.GetTLSProfileSpec(nil)  // Intermediate (TLS 1.2+)
+    fallback, fallbackErr := tlspkg.GetTLSProfileSpec(nil)
+    if fallbackErr != nil {
+        return fmt.Errorf("get Intermediate TLS profile: %w", fallbackErr)
+    }
+    profileSpec = fallback
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
profileSpec, err := tlspkg.FetchAPIServerTLSProfile(ctx, hubClient)
if err != nil { // fallback for kind/non-OpenShift clusters
profileSpec, _ = tlspkg.GetTLSProfileSpec(nil) // Intermediate (TLS 1.2+)
}
profileSpec, err := tlspkg.FetchAPIServerTLSProfile(ctx, hubClient)
if err != nil { // fallback for kind/non-OpenShift clusters
fallback, fallbackErr := tlspkg.GetTLSProfileSpec(nil)
if fallbackErr != nil {
return fmt.Errorf("get Intermediate TLS profile: %w", fallbackErr)
}
profileSpec = fallback
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` around lines 81 - 84, Update the documented TLS profile fallback
around FetchAPIServerTLSProfile and GetTLSProfileSpec so it never discards the
fallback error. Handle the returned error explicitly, wrapping it with
contextual information and returning or propagating it, or reference a helper
that validates and returns the fallback profile.

Source: Path instructions

Comment thread test/e2e/hcp_proxy_test.go
Comment thread .github/workflows/e2e-hcp-proxy.yaml Outdated
Comment thread .vscode/launch.json
Comment thread pkg/manager/hcp_proxy.go Outdated
Comment thread pkg/manager/hcp_proxy.go Outdated

@kurwang kurwang 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.

added some comments this is an awesome pr

Implement the hub-side HCP proxy so platform engineers can create a
HostedCluster (plus NodePools and Secrets) on a managed hosting cluster
using hub credentials, without a per-cluster kubeconfig (ACM-37268).

Creation order: Namespace → Secrets → HostedCluster → NodePool(s) via
cluster-proxy, with managedcluster:admin auth, ManagedCluster health
checks, duplicate-name handling, DNS-1123 path sanitization, and
hcp.ocm.io/created-via labels. Includes unit/e2e coverage and TLS
profile compliance for the proxy server.

Signed-off-by: yiraeChristineKim <yikim@redhat.com>
Co-Authored-By: Cursor <cursoragent@cursor.com>
Avoid a race where kubectl wait fails with NotFound when placement
has not yet created the ManagedClusterAddOn after OCM join.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud

Copy link
Copy Markdown

@kurwang kurwang 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.

/lgtm

@openshift-ci

openshift-ci Bot commented Jul 22, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: kurwang, yiraeChristineKim

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [kurwang,yiraeChristineKim]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-bot
openshift-merge-bot Bot merged commit e31fa67 into stolostron:main Jul 22, 2026
12 checks passed
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.

2 participants