Skip to content

fix: support multi-file KUBECONFIG with three-way merge#970

Merged
yxxhero merged 2 commits intomasterfrom
fix/multi-file-kubeconfig-three-way-merge
Apr 10, 2026
Merged

fix: support multi-file KUBECONFIG with three-way merge#970
yxxhero merged 2 commits intomasterfrom
fix/multi-file-kubeconfig-three-way-merge

Conversation

@yxxhero
Copy link
Copy Markdown
Collaborator

@yxxhero yxxhero commented Apr 10, 2026

Summary

Fixes #969

Multi-file KUBECONFIG (e.g., KUBECONFIG=/path/a:/path/b:/path/c) failed with HELM_DIFF_THREE_WAY_MERGE=true due to the entire colon-separated string being passed as ExplicitPath to the k8s client config loader, which called os.Stat() on the unsplit path.

  • Extracted prepareEnvSettings() helper that creates a fresh EnvSettings and clears KubeConfig when it contains multiple paths, allowing the default loading rules to handle multi-file KUBECONFIG via the Precedence field
  • This also fixes the --kube-context flag being silently ignored in the three-way merge path (the previous shallow copy *localEnv = *envSettings left ConfigFlags.Context pointing to the wrong struct)
  • Added 9 test cases covering single/multi-file KUBECONFIG, kube-context overrides, and ExplicitPath verification

The three-way merge code path used a shallow copy of EnvSettings which
caused ConfigFlags.KubeConfig to point to the wrong struct. When
KUBECONFIG contained multiple colon-separated paths, the entire string
was passed as ExplicitPath to the k8s client loader, causing os.Stat()
to fail with 'no such file or directory'.

Fix by creating a fresh EnvSettings and clearing KubeConfig when it
contains multiple paths, allowing the default loading rules to handle
multi-file KUBECONFIG via the Precedence field.

Signed-off-by: yxxhero <aiopsclub@163.com>
@yxxhero
Copy link
Copy Markdown
Collaborator Author

yxxhero commented Apr 10, 2026

@copilot please fix ci issue.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes the three-way-merge code path in helm diff upgrade so it works with multi-file KUBECONFIG values (path-list) and correctly honors --kube-context when initializing the Kubernetes client.

Changes:

  • Replace the shallow-copy EnvSettings setup with a dedicated prepareEnvSettings() helper to avoid miswired ConfigFlags pointers and to clear KubeConfig when it contains multiple paths.
  • Add unit tests covering single vs multi-file KUBECONFIG handling and kube-context override behavior.

Reviewed changes

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

File Description
cmd/upgrade.go Introduces prepareEnvSettings() and uses it for three-way-merge/take-ownership action initialization to support multi-file KUBECONFIG and correct kube-context wiring.
cmd/upgrade_test.go Adds tests for prepareEnvSettings() behavior across single/multi-file kubeconfig and kube-context override scenarios.

Comment on lines +69 to +72
func TestPrepareEnvSettings_MultiFileKubeconfig(t *testing.T) {
original := os.Getenv("KUBECONFIG")
defer os.Setenv("KUBECONFIG", original)

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

The test restores KUBECONFIG via original := os.Getenv(...) + defer os.Setenv(...), which can leave KUBECONFIG set to an empty string even if it was originally unset. Prefer t.Setenv (per subtest) or use os.LookupEnv and os.Unsetenv to restore the pre-test state accurately (and avoid ignoring os.Setenv errors).

Copilot uses AI. Check for mistakes.

if env.KubeContext != "my-override-context" {
t.Errorf("env.KubeContext = %q, want %q", env.KubeContext, "my-override-context")
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

Subtest name suggests it verifies ConfigFlags/RESTClientGetter wiring, but it only asserts env.KubeContext. To catch the original regression, also assert that the context used by the REST client loader reflects the override (e.g., env.ConfigFlags.Context points to the right value and/or the raw kubeconfig loader resolves the expected current context).

Suggested change
}
}
if env.ConfigFlags == nil {
t.Fatal("env.ConfigFlags is nil")
}
if env.ConfigFlags.Context == nil {
t.Fatal("env.ConfigFlags.Context is nil")
}
if got := *env.ConfigFlags.Context; got != "my-override-context" {
t.Errorf("env.ConfigFlags.Context = %q, want %q", got, "my-override-context")
}
getter := env.RESTClientGetter()
rawLoader := getter.ToRawKubeConfigLoader()
rawCfg, err := rawLoader.RawConfig()
if err != nil {
t.Fatalf("RawConfig() returned error: %v", err)
}
if rawCfg.CurrentContext != "my-override-context" {
t.Errorf("raw kubeconfig CurrentContext = %q, want %q", rawCfg.CurrentContext, "my-override-context")
}

Copilot uses AI. Check for mistakes.

if env.KubeConfig != "/tmp/single-config" {
t.Errorf("env.KubeConfig = %q, want %q", env.KubeConfig, "/tmp/single-config")
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

This subtest says it preserves ExplicitPath, but it only checks env.KubeConfig. Consider also asserting that env.RESTClientGetter().ToRawKubeConfigLoader().ConfigAccess().GetExplicitFile() equals the single kubeconfig path so the test actually validates ExplicitPath behavior.

Suggested change
}
}
getter := env.RESTClientGetter()
rawConfig := getter.ToRawKubeConfigLoader()
loadingRules := rawConfig.ConfigAccess()
if loadingRules == nil {
t.Fatal("ConfigAccess() = nil, want non-nil for single-file KUBECONFIG")
}
if explicitPath := loadingRules.GetExplicitFile(); explicitPath != "/tmp/single-config" {
t.Errorf("ExplicitPath = %q, want %q", explicitPath, "/tmp/single-config")
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

@copilot please fix ci issue.

Fixed in ddf0832. The lint failure was caused by the unused global var envSettings = cli.New() that was left over after I introduced the prepareEnvSettings() helper function. Removed the dead variable.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

@yxxhero yxxhero merged commit 5b3fdee into master Apr 10, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Multi-file KUBECONFIG fails with HELM_DIFF_THREE_WAY_MERGE=true - regression in v3.14.0

3 participants