fix: pass --kubeconfig to chartify's helm template call (#2449)
When using jsonPatches or kustomize patches with helmfile, chartify runs "helm template" internally to render the chart before applying patches. The lookup() helm function requires cluster access (--dry-run=server). Previously, --kubeconfig was passed to helm diff and helm upgrade commands, but not to chartify's internal helm template call. This caused failures when users specified --kubeconfig flag with a non-default kubeconfig location. This fix ensures --kubeconfig is passed to chartify's TemplateArgs for cluster-requiring commands (sync, apply, diff, etc.), alongside the existing --kube-context and --dry-run=server flags. Fixes #2444 Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
parent
ce09f560d9
commit
615e8132ee
|
|
@ -0,0 +1,195 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestKubeconfigPassedToChartify verifies that when --kubeconfig is set,
|
||||
// it is passed to chartify's internal helm template call.
|
||||
// This is a regression test for issue #2444.
|
||||
//
|
||||
// Background: When using jsonPatches or kustomize patches with helmfile,
|
||||
// chartify runs "helm template" internally to render the chart before applying patches.
|
||||
// The lookup() helm function requires cluster access (--dry-run=server).
|
||||
// Without --kubeconfig being passed to the internal helm template call,
|
||||
// it fails to connect to the cluster when the user's kubeconfig is not in the default location.
|
||||
func TestKubeconfigPassedToChartify(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
helmfileCommand string
|
||||
kubeconfig string
|
||||
kubeContext string
|
||||
expectedFlags []string
|
||||
unexpectedFlags []string
|
||||
}{
|
||||
{
|
||||
name: "sync with kubeconfig should pass both kubeconfig and dry-run=server",
|
||||
helmfileCommand: "sync",
|
||||
kubeconfig: "/path/to/kubeconfig",
|
||||
kubeContext: "",
|
||||
expectedFlags: []string{"--kubeconfig", "/path/to/kubeconfig", "--dry-run=server"},
|
||||
unexpectedFlags: []string{},
|
||||
},
|
||||
{
|
||||
name: "sync with kubeconfig and kube-context should pass both",
|
||||
helmfileCommand: "sync",
|
||||
kubeconfig: "/path/to/kubeconfig",
|
||||
kubeContext: "my-context",
|
||||
expectedFlags: []string{"--kubeconfig", "/path/to/kubeconfig", "--kube-context", "my-context", "--dry-run=server"},
|
||||
unexpectedFlags: []string{},
|
||||
},
|
||||
{
|
||||
name: "apply with kubeconfig should pass kubeconfig",
|
||||
helmfileCommand: "apply",
|
||||
kubeconfig: "/custom/kubeconfig",
|
||||
kubeContext: "",
|
||||
expectedFlags: []string{"--kubeconfig", "/custom/kubeconfig", "--dry-run=server"},
|
||||
unexpectedFlags: []string{},
|
||||
},
|
||||
{
|
||||
name: "diff with kubeconfig should pass kubeconfig",
|
||||
helmfileCommand: "diff",
|
||||
kubeconfig: "/etc/kubeconfig",
|
||||
kubeContext: "prod",
|
||||
expectedFlags: []string{"--kubeconfig", "/etc/kubeconfig", "--kube-context", "prod", "--dry-run=server"},
|
||||
unexpectedFlags: []string{},
|
||||
},
|
||||
{
|
||||
name: "template command should not pass kubeconfig (offline command)",
|
||||
helmfileCommand: "template",
|
||||
kubeconfig: "/path/to/kubeconfig",
|
||||
kubeContext: "",
|
||||
expectedFlags: []string{},
|
||||
unexpectedFlags: []string{"--kubeconfig", "--dry-run=server"},
|
||||
},
|
||||
{
|
||||
name: "build command should not pass kubeconfig (offline command)",
|
||||
helmfileCommand: "build",
|
||||
kubeconfig: "/path/to/kubeconfig",
|
||||
kubeContext: "",
|
||||
expectedFlags: []string{},
|
||||
unexpectedFlags: []string{"--kubeconfig", "--dry-run=server"},
|
||||
},
|
||||
{
|
||||
name: "no kubeconfig should not add kubeconfig flag",
|
||||
helmfileCommand: "sync",
|
||||
kubeconfig: "",
|
||||
kubeContext: "my-context",
|
||||
expectedFlags: []string{"--kube-context", "my-context", "--dry-run=server"},
|
||||
unexpectedFlags: []string{"--kubeconfig"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
templateArgs := buildChartifyTemplateArgs(tt.helmfileCommand, tt.kubeconfig, tt.kubeContext, false, "")
|
||||
|
||||
for _, flag := range tt.expectedFlags {
|
||||
if !strings.Contains(templateArgs, flag) {
|
||||
t.Errorf("buildChartifyTemplateArgs() = %q; want to contain %q", templateArgs, flag)
|
||||
}
|
||||
}
|
||||
|
||||
for _, flag := range tt.unexpectedFlags {
|
||||
if strings.Contains(templateArgs, flag) {
|
||||
t.Errorf("buildChartifyTemplateArgs() = %q; want NOT to contain %q", templateArgs, flag)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestKubeconfigNotDuplicated verifies that kubeconfig is not duplicated
|
||||
// when it already exists in the template args.
|
||||
func TestKubeconfigNotDuplicated(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
helmfileCommand string
|
||||
kubeconfig string
|
||||
existingArgs string
|
||||
expectedCount int
|
||||
expectedContains string
|
||||
}{
|
||||
{
|
||||
name: "do not duplicate kubeconfig",
|
||||
helmfileCommand: "sync",
|
||||
kubeconfig: "/path/to/kubeconfig",
|
||||
existingArgs: "--kubeconfig /existing/kubeconfig",
|
||||
expectedCount: 1,
|
||||
expectedContains: "--kubeconfig /existing/kubeconfig",
|
||||
},
|
||||
{
|
||||
name: "add kubeconfig when not present",
|
||||
helmfileCommand: "sync",
|
||||
kubeconfig: "/path/to/kubeconfig",
|
||||
existingArgs: "--some-flag",
|
||||
expectedCount: 1,
|
||||
expectedContains: "--kubeconfig /path/to/kubeconfig",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
templateArgs := buildChartifyTemplateArgs(tt.helmfileCommand, tt.kubeconfig, "", false, tt.existingArgs)
|
||||
|
||||
if !strings.Contains(templateArgs, tt.expectedContains) {
|
||||
t.Errorf("buildChartifyTemplateArgs() = %q; want to contain %q", templateArgs, tt.expectedContains)
|
||||
}
|
||||
|
||||
count := strings.Count(templateArgs, "--kubeconfig")
|
||||
if count != tt.expectedCount {
|
||||
t.Errorf("buildChartifyTemplateArgs() has --kubeconfig %d times; want %d", count, tt.expectedCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// buildChartifyTemplateArgs simulates the logic from processChartification
|
||||
// for building template args passed to chartify.
|
||||
// This helper encapsulates the flag-building logic for testing.
|
||||
//
|
||||
// NOTE: This function intentionally duplicates the logic from processChartification()
|
||||
// in state.go (lines 1549-1602). See issue_2355_test.go for rationale on this duplication.
|
||||
//
|
||||
// SYNC WARNING: If the flag-building logic in processChartification() changes
|
||||
// (state.go lines 1549-1602), this function must be updated to match.
|
||||
func buildChartifyTemplateArgs(helmfileCommand, kubeconfig, kubeContext string, validate bool, existingTemplateArgs string) string {
|
||||
var requiresCluster bool
|
||||
switch helmfileCommand {
|
||||
case "diff", "apply", "sync", "destroy", "delete", "test", "status":
|
||||
requiresCluster = true
|
||||
case "template", "lint", "build", "pull", "fetch", "write-values", "list", "show-dag", "deps", "repos", "cache", "init", "completion", "help", "version":
|
||||
requiresCluster = false
|
||||
default:
|
||||
requiresCluster = true
|
||||
}
|
||||
|
||||
templateArgs := existingTemplateArgs
|
||||
|
||||
if requiresCluster {
|
||||
var additionalArgs []string
|
||||
|
||||
if kubeconfig != "" && !strings.Contains(templateArgs, "--kubeconfig") {
|
||||
additionalArgs = append(additionalArgs, "--kubeconfig", kubeconfig)
|
||||
}
|
||||
|
||||
if kubeContext != "" && !strings.Contains(templateArgs, "--kube-context") {
|
||||
additionalArgs = append(additionalArgs, "--kube-context", kubeContext)
|
||||
}
|
||||
|
||||
if !validate && !strings.Contains(templateArgs, "--dry-run") {
|
||||
additionalArgs = append(additionalArgs, "--dry-run=server")
|
||||
}
|
||||
|
||||
if len(additionalArgs) > 0 {
|
||||
if templateArgs == "" {
|
||||
templateArgs = strings.Join(additionalArgs, " ")
|
||||
} else {
|
||||
templateArgs += " " + strings.Join(additionalArgs, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return templateArgs
|
||||
}
|
||||
|
|
@ -1579,6 +1579,11 @@ func (st *HelmState) processChartification(chartification *Chartify, release *Re
|
|||
// Build the additional args needed for cluster-requiring commands
|
||||
var additionalArgs []string
|
||||
|
||||
// Add --kubeconfig if configured (Issue #2444)
|
||||
if st.kubeconfig != "" && !strings.Contains(chartifyOpts.TemplateArgs, "--kubeconfig") {
|
||||
additionalArgs = append(additionalArgs, "--kubeconfig", st.kubeconfig)
|
||||
}
|
||||
|
||||
// Add --kube-context if configured (Issue #2309)
|
||||
// Note: kube-context is independent of the validate/dry-run mutual exclusion
|
||||
if kubeContext != "" && !strings.Contains(chartifyOpts.TemplateArgs, "--kube-context") {
|
||||
|
|
|
|||
Loading…
Reference in New Issue