Skip to content

CNTRLPLANE-596: Add --kubeconfig flag to HyperShift and HCP CLI - #8402

Merged
openshift-merge-bot[bot] merged 2 commits into
openshift:mainfrom
hypershift-community:fix-CNTRLPLANE-596
Jun 25, 2026
Merged

CNTRLPLANE-596: Add --kubeconfig flag to HyperShift and HCP CLI#8402
openshift-merge-bot[bot] merged 2 commits into
openshift:mainfrom
hypershift-community:fix-CNTRLPLANE-596

Conversation

@hypershift-jira-solve-ci

@hypershift-jira-solve-ci hypershift-jira-solve-ci Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Adds an explicit --kubeconfig flag to the create cluster and destroy cluster commands in both the hypershift and hcp CLIs.

Previously, users managing multiple HyperShift management clusters had to modify their default kubeconfig or set the KUBECONFIG environment variable before running CLI commands. The new --kubeconfig flag provides a more direct and scriptable approach to target a specific management cluster. When the flag is not provided, the CLI falls back to the default kubeconfig resolution (KUBECONFIG env var, then ~/.kube/config).

Key changes:

  • Add GetConfigWithKubeconfig, GetClientWithKubeconfig, and GetKubeClientSetWithKubeconfig functions that accept an explicit kubeconfig path, falling back to default resolution when empty
  • Bind --kubeconfig as a persistent flag on both create cluster and destroy cluster parent commands (shared across hypershift and hcp CLIs via bindCoreOptions and direct flag registration)
  • Thread the kubeconfig path through all internal call sites in create and destroy flows, including AWS GetSecret calls that previously bypassed the flag
  • Refactor existing GetConfig, GetClient, and GetKubeClientSet to delegate to their WithKubeconfig variants to eliminate duplication

Which issue(s) this PR fixes:

Fixes https://redhat.atlassian.net/browse/CNTRLPLANE-596

Special notes for your reviewer:

  • The --kubeconfig flag is only added to create cluster and destroy cluster commands, not to all subcommands. This is intentional to limit scope.
  • GetAPIServerAddressByNode was changed from variadic ...string to a plain string parameter for the kubeconfig path, since it always receives exactly one value.

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Always review AI generated responses prior to use.
Generated with Claude Code via /jira:solve [CNTRLPLANE-596](https://redhat.atlassian.net/browse/CNTRLPLANE-596)


Note: This PR was auto-generated by the jira-agent periodic CI job in response to CNTRLPLANE-596. See the full report for token usage, cost breakdown, and detailed phase output.

Summary by CodeRabbit

  • New Features

    • Added a persistent --kubeconfig flag for cluster commands to specify an explicit kubeconfig.
    • Cluster workflows now respect the provided kubeconfig for API server address resolution, client creation, and credential validation across create/destroy flows.
    • AWS credential validation and secret retrieval now use kubeconfig-aware clients when applicable.
  • Tests

    • Added unit tests for kubeconfig-aware config/client helpers and kubeconfig-based clientset creation.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 4, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label May 4, 2026
@openshift-ci-robot

openshift-ci-robot commented May 4, 2026

Copy link
Copy Markdown

@hypershift-jira-solve-ci[bot]: This pull request references CNTRLPLANE-596 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:

Adds an explicit --kubeconfig flag to the create cluster and destroy cluster commands in both the hypershift and hcp CLIs.

Previously, users managing multiple HyperShift management clusters had to modify their default kubeconfig or set the KUBECONFIG environment variable before running CLI commands. The new --kubeconfig flag provides a more direct and scriptable approach to target a specific management cluster. When the flag is not provided, the CLI falls back to the default kubeconfig resolution (KUBECONFIG env var, then ~/.kube/config).

Key changes:

  • Add GetConfigWithKubeconfig, GetClientWithKubeconfig, and GetKubeClientSetWithKubeconfig functions that accept an explicit kubeconfig path, falling back to default resolution when empty
  • Bind --kubeconfig as a persistent flag on both create cluster and destroy cluster parent commands (shared across hypershift and hcp CLIs via bindCoreOptions and direct flag registration)
  • Thread the kubeconfig path through all internal call sites in create and destroy flows, including AWS GetSecret calls that previously bypassed the flag
  • Refactor existing GetConfig, GetClient, and GetKubeClientSet to delegate to their WithKubeconfig variants to eliminate duplication

Which issue(s) this PR fixes:

Fixes https://redhat.atlassian.net/browse/CNTRLPLANE-596

Special notes for your reviewer:

  • The --kubeconfig flag is only added to create cluster and destroy cluster commands, not to all subcommands. This is intentional to limit scope.
  • GetAPIServerAddressByNode was changed from variadic ...string to a plain string parameter for the kubeconfig path, since it always receives exactly one value.

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Always review AI generated responses prior to use.
Generated with Claude Code via /jira:solve [CNTRLPLANE-596](https://redhat.atlassian.net/browse/CNTRLPLANE-596)

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

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

Walkthrough

Adds explicit kubeconfig-path support across CLI, command handlers, and utilities. New helpers GetConfigWithKubeconfig(kubeconfigPath string) and GetClientWithKubeconfig(kubeconfigPath string) build REST configs/clients from an explicit kubeconfig path (falling back to controller-runtime defaults when empty). Commands register a persistent --kubeconfig flag and options structs gain Kubeconfig fields. Cluster creation/destroy and platform handlers (AWS, KubeVirt, Agent, None) now pass the kubeconfig path into client construction, API-server address resolution, and secret/credential validation; related function signatures and tests were updated.

Sequence Diagram(s)

sequenceDiagram
  participant User as CLI (user)
  participant Cmd as Command logic (core/aws/agent/none)
  participant Util as cmd/util (GetClientWithKubeconfig / GetConfigWithKubeconfig)
  participant K8s as Kubernetes API (apiserver)

  User->>Cmd: invoke command (--kubeconfig optional)
  Cmd->>Util: GetClientWithKubeconfig(kubeconfigPath)
  alt kubeconfigPath != ""
    Util->>Util: clientcmd.BuildConfigFromFlags("", kubeconfigPath)
    Note right of Util: wrap errors with kubeconfig path context
  else kubeconfigPath == ""
    Util->>Util: controller-runtime GetConfig() / env resolution
  end
  Util->>K8s: create controller-runtime client using config
  Cmd->>K8s: API calls (Get Secret / Read Nodes / Validate resources)
  K8s-->>Cmd: Secret / Node / Resource responses
  Cmd-->>User: result (validation / create / destroy)
Loading

Suggested reviewers

  • enxebre
🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a --kubeconfig flag to HyperShift and HCP CLI commands, which is the primary objective reflected throughout the changeset.
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 test names in modified/added test files are static, descriptive, and deterministic. No dynamic content patterns (UUIDs, timestamps, generated identifiers, pod/node names) found in any test titles.
Test Structure And Quality ✅ Passed Custom check requires reviewing Ginkgo test code. PR adds standard Go unit tests using testing.T and Gomega, not Ginkgo. Check is not applicable.
Microshift Test Compatibility ✅ Passed No Ginkgo e2e tests were added in this PR. All test additions are standard Go unit tests using testing.T. The custom check does not apply to this PR.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests are added in this PR. The changes are limited to unit tests for utility functions and updated test call sites. The custom check is not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed Not applicable. PR modifies CLI tooling only (kubeconfig flag, client utilities). No deployment manifests, operator code, controllers, or scheduling constraints.
Ote Binary Stdout Contract ✅ Passed No process-level code (main, init, TestMain, BeforeSuite) was added. Only fmt.Printf is in CreateCluster function (acceptable). Tests use Gomega (GinkgoWriter) and helpers write to temp files only.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests added in this PR. All test files use standard Go testing.T package with unit tests. Custom check for IPv6/disconnected network compatibility of Ginkgo e2e tests is not applicable.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@openshift-ci

openshift-ci Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci openshift-ci Bot added area/cli Indicates the PR includes changes for CLI area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/aws PR/issue for AWS (AWSPlatform) platform area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform area/platform/none PR/issue for None (NonePlatform) platform - user-supplied infrastructure and removed do-not-merge/needs-area labels May 4, 2026
@codecov

codecov Bot commented May 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 51.72414% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.60%. Comparing base (7eea4bd) to head (9ab3ce7).
⚠️ Report is 13 commits behind head on main.

Files with missing lines Patch % Lines
cmd/cluster/core/create.go 9.09% 10 Missing ⚠️
cmd/cluster/aws/destroy.go 41.66% 7 Missing ⚠️
cmd/util/client.go 82.35% 2 Missing and 1 partial ⚠️
cmd/cluster/aws/create.go 60.00% 1 Missing and 1 partial ⚠️
cmd/cluster/agent/create.go 0.00% 1 Missing ⚠️
cmd/cluster/cluster.go 0.00% 1 Missing ⚠️
cmd/cluster/kubevirt/create.go 0.00% 1 Missing ⚠️
cmd/cluster/none/create.go 0.00% 1 Missing ⚠️
product-cli/cmd/cluster/aws/destroy.go 0.00% 1 Missing ⚠️
support/util/util.go 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8402      +/-   ##
==========================================
+ Coverage   42.50%   42.60%   +0.09%     
==========================================
  Files         768      768              
  Lines       95272    95322      +50     
==========================================
+ Hits        40498    40609     +111     
+ Misses      51971    51903      -68     
- Partials     2803     2810       +7     
Files with missing lines Coverage Δ
cmd/cluster/core/destroy.go 12.10% <100.00%> (+3.18%) ⬆️
product-cli/cmd/cluster/cluster.go 100.00% <100.00%> (ø)
cmd/cluster/agent/create.go 58.58% <0.00%> (ø)
cmd/cluster/cluster.go 0.00% <0.00%> (ø)
cmd/cluster/kubevirt/create.go 54.20% <0.00%> (ø)
cmd/cluster/none/create.go 54.54% <0.00%> (ø)
product-cli/cmd/cluster/aws/destroy.go 68.75% <0.00%> (ø)
support/util/util.go 42.04% <80.00%> (+2.33%) ⬆️
cmd/cluster/aws/create.go 41.59% <60.00%> (ø)
cmd/util/client.go 48.21% <82.35%> (+48.21%) ⬆️
... and 2 more

... and 3 files with indirect coverage changes

Flag Coverage Δ
cmd-support 35.59% <51.78%> (+0.13%) ⬆️
cpo-hostedcontrolplane 44.84% <ø> (ø)
cpo-other 44.70% <ø> (+0.37%) ⬆️
hypershift-operator 53.05% <ø> (-0.01%) ⬇️
other 31.69% <50.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/cluster/core/create.go (1)

651-653: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid wrapping a nil error when no nodes are returned

On Line 652, %w wraps err, but err is nil at that point. This produces a misleading message and drops useful context.

🛠️ Suggested fix
 	if len(nodes.Items) < 1 {
-		return "", fmt.Errorf("no node objects found: %w", err)
+		return "", fmt.Errorf("no node objects found")
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/cluster/core/create.go` around lines 651 - 653, The error return in the
check for nodes.Items incorrectly uses fmt.Errorf("no node objects found: %w",
err) while err is nil; update the return in the nodes length check to not wrap a
nil error — e.g. return "", fmt.Errorf("no node objects found") — or, if there
is an actual underlying error to surface, use that concrete error variable
instead of err; adjust the code around the nodes/Items check in create.go (the
block referencing nodes.Items and err) accordingly.
🧹 Nitpick comments (1)
cmd/cluster/aws/destroy_test.go (1)

17-39: ⚡ Quick win

Add one kubeconfig-focused test case to validate the new parameter behavior.

Line 44 now passes kubeconfig, but current cases don’t actually assert any kubeconfig-dependent path. Add a case with CredentialSecretName set and an invalid kubeconfig path (or valid temp kubeconfig) to verify the new argument is functionally covered.

Also applies to: 44-44

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/cluster/aws/destroy_test.go` around lines 17 - 39, Add a new test case in
the table in cmd/cluster/aws/destroy_test.go that covers the kubeconfig path
handling by setting DestroyOptions.CredentialSecretName to a non-empty value and
providing a kubeconfig path (either an invalid path expecting expectError=true
or a created temp kubeconfig file expecting expectError=false); reference the
existing test map entries that use core.DestroyOptions,
core.AWSPlatformDestroyOptions and awsutil.AWSCredentialsOptions so the new case
exercises the code path that reads/validates kubeconfig (use a temp file
creation helper if you need a valid kubeconfig) and set expectError to match the
intended behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/util/client_test.go`:
- Around line 49-51: The table-driven test case named "When kubeconfig path is
empty, it should fall back to default config resolution" currently allows err !=
nil to pass silently; update the test in cmd/util/client_test.go so that for the
empty kubeconfigPath case you assert no error is returned (err == nil) and
validate the returned client/config is non-nil (or matches the expected
default-resolution behavior) instead of allowing the branch that accepts any
error; likewise tighten the other cases around lines 81-91 to explicitly assert
success or failure as expected for each test entry (use the test case's name
string to identify each case and replace the permissive err!=nil branch with
explicit assertions).
- Around line 127-129: The test uses t.Setenv("FAKE_CLIENT","true") only when
tc.fakeClient is true, which can leak environment state across subtests; inside
the subtest for the test function (the loop handling tc), ensure FAKE_CLIENT is
explicitly set or cleared for every case by calling
t.Setenv("FAKE_CLIENT","true") when tc.fakeClient is true and
t.Setenv("FAKE_CLIENT","") or t.Setenv("FAKE_CLIENT","false") (or use
os.Unsetenv via t.Setenv with empty string) when tc.fakeClient is false so each
subtest (the code surrounding the if tc.fakeClient block) runs with an isolated
FAKE_CLIENT value.

In `@support/util/clientset_test.go`:
- Around line 74-81: The test currently uses "else if err == nil" which allows a
failing case (expectError==false but err!=nil) to slip through; in the test for
the table case (tc.expectError / tc.errorContains) replace the "else if err ==
nil" branch with an unconditional else that asserts err is nil and that the
returned kube client (kc) is not nil (and optionally assert no unexpected error
message), i.e., when tc.expectError is false assert err == nil and
g.Expect(kc).ToNot(BeNil()) so failures in success paths are caught.

---

Outside diff comments:
In `@cmd/cluster/core/create.go`:
- Around line 651-653: The error return in the check for nodes.Items incorrectly
uses fmt.Errorf("no node objects found: %w", err) while err is nil; update the
return in the nodes length check to not wrap a nil error — e.g. return "",
fmt.Errorf("no node objects found") — or, if there is an actual underlying error
to surface, use that concrete error variable instead of err; adjust the code
around the nodes/Items check in create.go (the block referencing nodes.Items and
err) accordingly.

---

Nitpick comments:
In `@cmd/cluster/aws/destroy_test.go`:
- Around line 17-39: Add a new test case in the table in
cmd/cluster/aws/destroy_test.go that covers the kubeconfig path handling by
setting DestroyOptions.CredentialSecretName to a non-empty value and providing a
kubeconfig path (either an invalid path expecting expectError=true or a created
temp kubeconfig file expecting expectError=false); reference the existing test
map entries that use core.DestroyOptions, core.AWSPlatformDestroyOptions and
awsutil.AWSCredentialsOptions so the new case exercises the code path that
reads/validates kubeconfig (use a temp file creation helper if you need a valid
kubeconfig) and set expectError to match the intended 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 YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 547a30e3-f175-4cf3-bb9b-49bfdf07969a

📥 Commits

Reviewing files that changed from the base of the PR and between 29053b7 and c9eebd3.

📒 Files selected for processing (17)
  • cmd/cluster/agent/create.go
  • cmd/cluster/aws/create.go
  • cmd/cluster/aws/create_test.go
  • cmd/cluster/aws/destroy.go
  • cmd/cluster/aws/destroy_test.go
  • cmd/cluster/cluster.go
  • cmd/cluster/core/create.go
  • cmd/cluster/core/destroy.go
  • cmd/cluster/kubevirt/create.go
  • cmd/cluster/none/create.go
  • cmd/util/client.go
  • cmd/util/client_test.go
  • product-cli/cmd/cluster/aws/destroy.go
  • product-cli/cmd/cluster/cluster.go
  • product-cli/cmd/cluster/cluster_test.go
  • support/util/clientset_test.go
  • support/util/util.go

Comment thread cmd/util/client_test.go Outdated
Comment thread cmd/util/client_test.go
Comment thread support/util/clientset_test.go

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/util/client.go`:
- Line 23: The KubeconfigFlagHelp string and GetConfigWithKubeconfig behavior
are inconsistent: the help text omits the in-cluster fallback while
GetConfigWithKubeconfig("") delegates to cr.GetConfig() which uses KUBECONFIG →
in-cluster → ~/.kube/config; either update the KubeconfigFlagHelp constant to
explicitly list the in-cluster fallback (e.g., "KUBECONFIG env var → in-cluster
config → ~/.kube/config") or change GetConfigWithKubeconfig (the function that
handles empty kubeconfig paths) to explicitly load kubeconfig using clientcmd
loading rules that match the advertised precedence (use
clientcmd.NewDefaultClientConfigLoadingRules or build a LoadingRules that
prefers KUBECONFIG then ~/.kube/config and avoid cr.GetConfig() in the
empty-path branch); apply the same change/wording to any uses in
support/util/util.go to keep behavior and docs consistent.
🪄 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 YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 377f8e67-0ad5-476f-88bc-0aad19d4daf9

📥 Commits

Reviewing files that changed from the base of the PR and between c9eebd3 and dfb18fa.

📒 Files selected for processing (12)
  • cmd/cluster/aws/create.go
  • cmd/cluster/aws/create_test.go
  • cmd/cluster/aws/destroy.go
  • cmd/cluster/aws/destroy_test.go
  • cmd/cluster/cluster.go
  • cmd/cluster/core/create.go
  • cmd/util/client.go
  • cmd/util/client_test.go
  • product-cli/cmd/cluster/aws/destroy.go
  • product-cli/cmd/cluster/cluster.go
  • support/util/clientset_test.go
  • support/util/util.go
✅ Files skipped from review due to trivial changes (2)
  • product-cli/cmd/cluster/cluster.go
  • cmd/cluster/aws/create_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • cmd/cluster/aws/destroy_test.go
  • cmd/util/client_test.go
  • support/util/clientset_test.go
  • cmd/cluster/cluster.go
  • cmd/cluster/aws/destroy.go
  • support/util/util.go
  • product-cli/cmd/cluster/aws/destroy.go

Comment thread cmd/util/client.go Outdated

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/cluster/core/create.go (1)

631-653: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don’t wrap a nil error when no nodes are returned.

At Line 652, %w wraps err, but err is nil on this path. Return a direct error message instead.

Suggested fix
-	if len(nodes.Items) < 1 {
-		return "", fmt.Errorf("no node objects found: %w", err)
-	}
+	if len(nodes.Items) < 1 {
+		return "", errors.New("no node objects found")
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/cluster/core/create.go` around lines 631 - 653, In
GetAPIServerAddressByNode, the code returns fmt.Errorf("no node objects found:
%w", err) while err is nil; change this to return a direct error message (e.g.
return "", fmt.Errorf("no node objects found")) so you don't wrap a nil error.
Locate the nodes.Items length check in GetAPIServerAddressByNode and replace the
faulty fmt.Errorf call with a plain error string or include relevant context
without using %w.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@cmd/cluster/core/create.go`:
- Around line 631-653: In GetAPIServerAddressByNode, the code returns
fmt.Errorf("no node objects found: %w", err) while err is nil; change this to
return a direct error message (e.g. return "", fmt.Errorf("no node objects
found")) so you don't wrap a nil error. Locate the nodes.Items length check in
GetAPIServerAddressByNode and replace the faulty fmt.Errorf call with a plain
error string or include relevant context without using %w.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 0118075a-4f36-4f58-a6bd-469e00e90bd3

📥 Commits

Reviewing files that changed from the base of the PR and between dfb18fa and bf93e3b.

📒 Files selected for processing (12)
  • cmd/cluster/aws/create.go
  • cmd/cluster/aws/create_test.go
  • cmd/cluster/aws/destroy.go
  • cmd/cluster/aws/destroy_test.go
  • cmd/cluster/cluster.go
  • cmd/cluster/core/create.go
  • cmd/util/client.go
  • cmd/util/client_test.go
  • product-cli/cmd/cluster/aws/destroy.go
  • product-cli/cmd/cluster/cluster.go
  • support/util/clientset_test.go
  • support/util/util.go
✅ Files skipped from review due to trivial changes (3)
  • product-cli/cmd/cluster/cluster.go
  • support/util/clientset_test.go
  • cmd/util/client.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • cmd/cluster/aws/create_test.go
  • cmd/cluster/aws/destroy_test.go
  • cmd/cluster/cluster.go
  • cmd/util/client_test.go
  • cmd/cluster/aws/destroy.go

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Re: #8402 (review)

Good catch — fixed. At line 652, err is guaranteed nil after the preceding check, so wrapping it with %w was misleading. Changed to errors.New("no node objects found").


AI-assisted response via Claude Code

@openshift-ci

openshift-ci Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Stale PRs are closed after 21d of inactivity.

If this PR is still relevant, comment to refresh it or remove the stale label.
Mark the PR as fresh by commenting /remove-lifecycle stale.

If this PR is safe to close now please do so with /close.

/lifecycle stale

@openshift-ci openshift-ci Bot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jun 11, 2026
@bryan-cox

Copy link
Copy Markdown
Member

/remove-lifecycle stale

@openshift-ci openshift-ci Bot removed the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jun 17, 2026
@bryan-cox

Copy link
Copy Markdown
Member

rebase this PR

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Re: #8402 (comment)

Done. Rebased all 4 commits onto the latest upstream/main (383 new upstream commits incorporated). No conflicts — clean rebase. Build verified.


AI-assisted response via Claude Code

@bryan-cox

Copy link
Copy Markdown
Member

/rebase

Improve patch coverage for the --kubeconfig changes by adding tests for
ValidateCredentialInfo with credential secrets, BindOptions kubeconfig
flag parsing, GetCluster with invalid kubeconfig, and wrapper functions
GetConfig, GetClient, and GetKubeClientSet.

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

@bryan-cox bryan-cox left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/approve

@openshift-ci

openshift-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bryan-cox, hypershift-jira-solve-ci[bot]

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:

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

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 24, 2026
@csrwng
csrwng marked this pull request as ready for review June 24, 2026 13:14
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 24, 2026
@openshift-ci
openshift-ci Bot requested review from cblecker and csrwng June 24, 2026 13:15
@bryan-cox

Copy link
Copy Markdown
Member

/retest

@csrwng

csrwng commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Why are gcp and azure clusters not included in this change?

@bryan-cox

Copy link
Copy Markdown
Member

/address-review-comments

@bryan-cox

Copy link
Copy Markdown
Member

^testing this out some more

@github-actions

Copy link
Copy Markdown

🤖 Addressing review comments: workflow run

@github-actions

Copy link
Copy Markdown

GCP and Azure are included in this change. The --kubeconfig flag is bound as a persistent flag on the parent cluster create and cluster destroy commands (cmd/cluster/cluster.go:29,61 and product-cli/cmd/cluster/cluster.go:29,61), so it's automatically inherited by all platform subcommands including Azure and GCP.

The platform-specific file changes in this PR were only needed where platform code created its own Kubernetes clients directly — AWS had ValidateCredentialInfo and destroyPlatformSpecifics calling util.GetSecret/util.GetClient, and agent/kubevirt/none called core.GetAPIServerAddressByNode. Those functions were updated to accept the kubeconfig path.

Azure and GCP don't have any such platform-specific client creation — they delegate entirely to core.CreateCluster, core.GetCluster, and core.DestroyCluster, all of which were updated to use util.GetClientWithKubeconfig(opts.Kubeconfig) in this PR.


AI-assisted response via Claude Code

@csrwng

csrwng commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jun 24, 2026
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aks
/test e2e-aws
/test e2e-aws-upgrade-hypershift-operator
/test e2e-azure-v2-self-managed
/test e2e-kubevirt-aws-ovn-reduced
/test e2e-v2-aws
/test e2e-v2-gke

@bryan-cox

Copy link
Copy Markdown
Member

/verified by UT

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Jun 24, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@bryan-cox: This PR has been marked as verified by UT.

Details

In response to this:

/verified by UT

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@hypershift-jira-solve-ci

hypershift-jira-solve-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

All 12 COPY --from=builder steps succeeded, confirming the Go compilation stage completed successfully. The failure is exclusively in the second stage at step 17/19 when installing azure-cli from Microsoft's RPM repository.

Now I have all the evidence I need for a comprehensive report. All 6 jobs failed with the identical root cause:

Test Failure Analysis Complete

Job Information

  • Prow Jobs: All 6 jobs on PR CNTRLPLANE-596: Add --kubeconfig flag to HyperShift and HCP CLI #8402 (CNTRLPLANE-596: Add --kubeconfig flag to HyperShift and HCP CLI)
    • pull-ci-openshift-hypershift-main-e2e-aws (Build ID: 2069813711689748480)
    • pull-ci-openshift-hypershift-main-e2e-azure-v2-self-managed (Build ID: 2069813718379663360)
    • pull-ci-openshift-hypershift-main-e2e-v2-gke (Build ID: 2069813726797631488)
    • pull-ci-openshift-hypershift-main-e2e-aws-upgrade-hypershift-operator (Build ID: 2069813715863080960)
    • pull-ci-openshift-hypershift-main-e2e-aks (Build ID: 2069813709991055360)
    • pull-ci-openshift-hypershift-main-e2e-v2-aws (Build ID: 2069813723739983872)
  • Failed Step: hypershift-tests image build (Dockerfile.e2e, stage [2/2] STEP 17/19)
  • Build Failure Reason: DockerBuildFailed

Test Failure Analysis

Error

Error: Failed to download metadata for repo 'packages-microsoft-com-prod': repomd.xml GPG signature verification error: Bad GPG signature
Ignoring repositories: packages-microsoft-com-prod
No match for argument: azure-cli
Error: Unable to find a match: azure-cli

step hypershift-tests failed: error occurred handling build hypershift-tests-amd64:
the build hypershift-tests-amd64 failed after 8m46s with reason DockerBuildFailed: Dockerfile build strategy has failed.

Summary

All 6 CI jobs failed with an identical infrastructure error unrelated to the PR's code changes. The Go compilation stage succeeded — all binaries (test-e2e, hypershift, hypershift-cli, etc.) were built and copied into the second Docker stage. The failure occurred at Dockerfile.e2e step 17/19, which installs azure-cli from Microsoft's RPM repository. Microsoft's packages-microsoft-com-prod repository served metadata (repomd.xml) with a bad GPG signature, causing dnf to reject the repository entirely, which made the azure-cli package unresolvable. This is a transient upstream infrastructure issue with Microsoft's package signing — a /retest should resolve it once the repository metadata is fixed.

Root Cause

Category: External Infrastructure Failure (Microsoft RPM Repository GPG Signing)

The hypershift-tests Docker image (Dockerfile.e2e) has a step that installs the Azure CLI from Microsoft's RPM repository:

RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc && \
    dnf install -y https://packages.microsoft.com/config/rhel/9/packages-microsoft-prod.rpm && \
    mv /etc/yum.repos.d/microsoft-prod.repo /etc/yum.repos.art/ci/ && \
    dnf install -y azure-cli && \
    dnf clean all

The failure chain:

  1. The Microsoft GPG key was imported successfully (rpm --import)
  2. The Microsoft repository RPM was installed successfully
  3. When dnf tried to download and verify the repository metadata (repomd.xml), the GPG signature verification failed — the signature on the metadata did not match the imported key
  4. dnf correctly rejected and ignored the packages-microsoft-com-prod repository
  5. With the only repository carrying azure-cli disabled, dnf install -y azure-cli failed with "Unable to find a match"
  6. The non-zero exit code propagated up, failing the Docker build

This is NOT caused by the PR changes. The PR adds a --kubeconfig flag to HyperShift/HCP CLI commands — purely Go code changes across 19 files. The Go compilation completed successfully (all 12 binaries were built and copied to the second stage). The failure is in a dnf install step that has no relation to the Go source code.

Additionally, DNS instability was observed (curl: (6) Could not resolve host: static.redhat.com) but did not directly cause the failure.

Recommendations
  1. Retest the PR — Run /retest to retrigger all jobs. This is a transient upstream issue with Microsoft's RPM repository GPG signing. The repository metadata is typically re-signed within hours.

  2. If retests continue to fail — The Dockerfile.e2e could be updated to add --nogpgcheck to the dnf install -y azure-cli command as a temporary workaround, though this has security implications.

  3. No code changes needed in this PR — The --kubeconfig flag implementation compiled successfully across all 6 jobs. The Go compilation stage passed; only the external dependency installation step failed.

Evidence
Evidence Detail
Failed Step hypershift-tests image build — [2/2] STEP 17/19 in Dockerfile.e2e
Failure Reason DockerBuildFailed after 8m46s
GPG Error repomd.xml GPG signature verification error: Bad GPG signature on packages-microsoft-com-prod repository
Package Error No match for argument: azure-cli / Unable to find a match: azure-cli
Go Compilation ✅ Succeeded — all 12 binaries built and copied (test-e2e, hypershift, hypershift-cli, etc.)
Other Images hypershift, hypershift-operator, hypershift-cli, src images all built successfully
Jobs Affected All 6/6 jobs failed with identical error at identical Dockerfile step
DNS Issue Non-fatal curl: (6) Could not resolve host: static.redhat.com also observed
PR Code Changes 19 files modified — adds --kubeconfig flag to CLI commands (no Dockerfile changes)
Consistency All 6 jobs share the same CI namespace build (hypershift-tests-amd64) and failed identically

@cblecker cblecker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor note: GetImpersonatedClient (cmd/util/client.go) still calls GetConfig() directly while all other client-creation functions now delegate to their WithKubeconfig variants. This is fine since the dump command is out of scope, but worth noting if --kubeconfig is extended to other commands later.

g.Expect(receivedOpts.AzurePlatform.Cloud).To(Equal("AzurePublicCloud"))
})

t.Run("When kubeconfig is set it should use it for the client", func(t *testing.T) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test is named "When kubeconfig is set it should use it for the client" but sets Kubeconfig: "" and relies on FAKE_CLIENT=true, which short-circuits GetClientWithKubeconfig before the kubeconfig path is ever consulted. It ends up being functionally identical to the test above it (lines 14-44).

Consider either renaming it to reflect what it actually tests, or rewriting it to exercise the kubeconfig path — e.g., using writeTestKubeconfig to provide a real path and dropping FAKE_CLIENT.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! I'll follow up on this one.

// ValidateCredentialInfo validates if the credentials secret name is empty, the aws-creds or sts-creds mutually exclusive and are not empty; validates if
// the credentials secret is not empty, that it can be retrieved.
func ValidateCredentialInfo(opts awsutil.AWSCredentialsOptions, credentialSecretName, namespace string) error {
func ValidateCredentialInfo(opts awsutil.AWSCredentialsOptions, credentialSecretName, namespace, kubeconfigPath string) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit for a follow-up: ValidateCredentialInfo now has 3 consecutive string parameters (credentialSecretName, namespace, kubeconfigPath), and ValidateCreateCredentialInfo has 4. Transposing any two compiles cleanly but produces wrong behavior. A small params struct would make call sites self-documenting. Not blocking this PR — callers are all correct today.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! I'll follow up on this one.

@bryan-cox

Copy link
Copy Markdown
Member

/retest

1 similar comment
@cblecker

Copy link
Copy Markdown
Member

/retest

@hypershift-jira-solve-ci

hypershift-jira-solve-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

I now have all the evidence needed. Let me produce the final report.

Test Failure Analysis Complete

Job Information

  • Prow Job: pull-ci-openshift-hypershift-main-e2e-azure-v2-self-managed
  • Build ID: 2069832719667302400
  • Target: e2e-azure-v2-self-managed
  • Failed Test: [sig-hypershift][Jira:Hypershift][Feature:EtcdResilience] Etcd Chaos [It] should recover after a member is removed from the etcd cluster
  • PR: CNTRLPLANE-596: Add --kubeconfig flag to HyperShift and HCP CLI #8402CNTRLPLANE-596: Add --kubeconfig flag to HyperShift and HCP CLI

Test Failure Analysis

Error

[FAILED] failed to remove etcd member etcd-2
Unexpected error:
    <exec.CodeExitError>:
    command terminated with exit code 1
occurred

etcdctl member remove error: "rpc error: code = Unavailable desc = etcdserver: unhealthy cluster"

Summary

The EtcdMissingMemberRecoveryTest failed because etcdctl member remove returned "etcdserver: unhealthy cluster" when attempting to remove etcd-2. This is a pre-existing test flake in the etcd chaos test suite — completely unrelated to PR #8402 which only adds a --kubeconfig CLI flag and touches zero etcd-related code. The etcd cluster had not fully stabilized from the preceding EtcdSingleMemberCorruptionTest, which destroyed etcd-1's data directory and triggered a recovery job that caused all three etcd members to be repeatedly killed and recreated within a ~2-minute window. When the member removal test started just ~80 seconds later, the cluster was still in a degraded state (etcd-2's readiness probe was failing at 18:43:37, and the test attempted the member remove at 18:44:57).

Root Cause

The root cause is a test sequencing/timing issue in the etcd chaos test suite, not a product bug or anything related to this PR.

Detailed sequence of events:

  1. 18:41:35 — Upgrade tests pass; etcd-chaos suite begins against the upgrade-f98c19d8e0 hosted cluster.
  2. 18:41:36 — The first three chaos tests (single member data loss, random kill, kill-all) complete quickly. During "kill all members," etcd-2 is killed at 18:41:36.
  3. 18:42:05–18:42:16data-etcd-2 PVC is being re-provisioned from scratch on Azure (took 11 seconds).
  4. 18:42:33–18:42:38etcd-2 pod starts its first recovery (1st container startup).
  5. 18:42:47etcd-2 is killed again (containers stopped) as the EtcdSingleMemberCorruptionTest begins. This test corrupts etcd-1's data directory, triggering the etcd recovery job, which causes all etcd pods to be recycled.
  6. 18:42:49–18:42:51etcd-2 pod starts its second recovery (2nd container startup in under 30 seconds).
  7. 18:43:02 — etcd-1 is also killed and restarted as part of the recovery job processing.
  8. 18:43:27etcd-2 is killed a third time within ~2 minutes.
  9. 18:43:28–18:43:31etcd-2 pod starts its third recovery (3rd container startup).
  10. 18:43:37etcd-2 readiness probe fails with context deadline exceeded — the etcd member had not finished joining the cluster yet.
  11. 18:44:56EtcdMissingMemberRecoveryTest starts. It successfully runs etcdctl member list to discover etcd-2's member ID (suggesting the etcd client endpoint was reachable).
  12. 18:44:57etcdctl member remove fails with "etcdserver: unhealthy cluster" — etcd refuses the membership change because the cluster hasn't achieved a healthy quorum after the turbulence. The test has no retry logic for the member remove operation and immediately fails.

Why etcd reported "unhealthy cluster": In etcd, a MemberRemove operation requires all members to be in a healthy, consistent state. With etcd-2 having been killed 3 times in 2 minutes and its readiness probe failing just 80 seconds before the removal attempt, the cluster's internal health checks were still detecting an unstable member. The member list succeeded because it's a read-only operation, but member remove is a consensus operation that requires a healthy cluster.

PR #8402 is not involved: The PR modifies only CLI flag handling for --kubeconfig in cmd/cluster/, cmd/util/, product-cli/, and support/util/. It touches zero test code, zero etcd code, zero operator code. The etcd chaos test failure is entirely an environment/timing issue within the test suite's sequential execution of destructive etcd operations.

Recommendations
  1. Retry this job — This is a flaky test unrelated to the PR's changes. A /retest should pass.
  2. File a test improvement issue — The EtcdMissingMemberRecoveryTest should add a pre-check that waits for the etcd cluster to be healthy before attempting member remove. A simple etcdctl endpoint health poll before the removal would prevent this race.
  3. Add stabilization delay between chaos tests — The Ordered Ginkgo suite runs tests back-to-back. After the corruption/recovery test, a brief wait for all etcd members' readiness probes to pass would eliminate this class of flake.
  4. Add retry logic to member remove — The test currently attempts member remove exactly once with no retry. Adding a retry loop with backoff (similar to how other chaos tests use EventuallyObject) would make this test resilient to transient cluster health issues.
Evidence
Evidence Detail
Failed test EtcdMissingMemberRecoveryTest at etcd_chaos_test.go:297
Error message etcdserver: unhealthy cluster on etcdctl member remove [etcd-2](https://redhat.atlassian.net/browse/etcd-2) (ID: 28eb0bad843ac5b1)
Test duration 0.879 seconds (failed immediately, no retry)
etcd-2 killed 3x 18:41:36, 18:42:47, 18:43:27 — three kills within 111 seconds
Readiness probe failure etcd-2 readiness probe failed at 18:43:37 (context deadline exceeded)
Time between readiness failure and member remove ~80 seconds (18:43:37 → 18:44:57)
PR #8402 changes CLI --kubeconfig flag in cmd/cluster/, cmd/util/, product-cli/ — zero etcd code touched
Grep for "etcd" in PR diff 0 matches
Other test groups All passed: upgrade (1 passed), autoscaling (8 passed), public/private/oauth-lb/external-oidc (426 passed)
Suite summary 4 Passed, 1 Failed, 1 Pending, 949 Skipped in etcd-chaos suite
Prow Job URL View in Prow

@openshift-ci

openshift-ci Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

@hypershift-jira-solve-ci: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@openshift-merge-bot
openshift-merge-bot Bot merged commit b487ae5 into openshift:main Jun 25, 2026
41 checks passed
@bryan-cox
bryan-cox deleted the fix-CNTRLPLANE-596 branch June 25, 2026 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. area/cli Indicates the PR includes changes for CLI area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/aws PR/issue for AWS (AWSPlatform) platform area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform area/platform/none PR/issue for None (NonePlatform) platform - user-supplied infrastructure jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants