-
Notifications
You must be signed in to change notification settings - Fork 33
feat: Allow using kubectl built-in kustomize when separate kustomize binary is missing #173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| package chartify | ||
|
|
||
| import ( | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // TestKubectlKustomize tests behavior when kubectl kustomize is explicitly configured | ||
| // via KustomizeBin("kubectl kustomize"). The automatic fallback selection is tested | ||
| // in TestKustomizeBin. | ||
| func TestKubectlKustomize(t *testing.T) { | ||
| t.Run("KustomizeBuild succeeds with kubectl kustomize option", func(t *testing.T) { | ||
| if _, err := exec.LookPath("kubectl"); err != nil { | ||
| t.Skip("kubectl binary not found in PATH") | ||
| } | ||
|
|
||
| tmpDir := t.TempDir() | ||
| srcDir := t.TempDir() | ||
|
|
||
| kustomizationContent := `apiVersion: kustomize.config.k8s.io/v1beta1 | ||
| kind: Kustomization | ||
| resources: | ||
| - deployment.yaml | ||
| ` | ||
| deploymentContent := `apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: test | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: test | ||
| template: | ||
| metadata: | ||
| labels: | ||
| app: test | ||
| spec: | ||
| containers: | ||
| - name: test | ||
| image: test:latest | ||
| ` | ||
|
|
||
| templatesDir := filepath.Join(tmpDir, "templates") | ||
| require.NoError(t, os.MkdirAll(templatesDir, 0755)) | ||
|
|
||
| require.NoError(t, os.WriteFile(filepath.Join(srcDir, "kustomization.yaml"), []byte(kustomizationContent), 0644)) | ||
| require.NoError(t, os.WriteFile(filepath.Join(srcDir, "deployment.yaml"), []byte(deploymentContent), 0644)) | ||
|
|
||
| r := New(KustomizeBin("kubectl kustomize")) | ||
|
|
||
| outputFile, err := r.KustomizeBuild(srcDir, tmpDir) | ||
| require.NoError(t, err) | ||
| require.FileExists(t, outputFile) | ||
| }) | ||
|
|
||
| t.Run("edit commands not supported with kubectl kustomize", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| srcDir := t.TempDir() | ||
|
|
||
| kustomizationContent := `apiVersion: kustomize.config.k8s.io/v1beta1 | ||
| kind: Kustomization | ||
| resources: | ||
| - deployment.yaml | ||
| ` | ||
| deploymentContent := `apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: test | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: test | ||
| template: | ||
| metadata: | ||
| labels: | ||
| app: test | ||
| spec: | ||
| containers: | ||
| - name: test | ||
| image: test:latest | ||
| ` | ||
|
|
||
| templatesDir := filepath.Join(tmpDir, "templates") | ||
| valuesDir := t.TempDir() | ||
| valuesFile := filepath.Join(valuesDir, "values.yaml") | ||
| valuesContent := `images: | ||
| - name: test | ||
| newName: newtest | ||
| newTag: v2 | ||
| ` | ||
|
|
||
| require.NoError(t, os.MkdirAll(templatesDir, 0755)) | ||
| require.NoError(t, os.WriteFile(filepath.Join(srcDir, "kustomization.yaml"), []byte(kustomizationContent), 0644)) | ||
| require.NoError(t, os.WriteFile(filepath.Join(srcDir, "deployment.yaml"), []byte(deploymentContent), 0644)) | ||
| require.NoError(t, os.WriteFile(valuesFile, []byte(valuesContent), 0644)) | ||
|
|
||
| r := New(KustomizeBin("kubectl kustomize")) | ||
|
|
||
| _, err := r.KustomizeBuild(srcDir, tmpDir, &KustomizeBuildOpts{ValuesFiles: []string{valuesFile}}) | ||
| require.Error(t, err) | ||
| require.Contains(t, err.Error(), "setting images via kustomizeOpts.Images is not supported when using 'kubectl kustomize'") | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -181,9 +181,13 @@ resources: | |||||||||||
|
|
||||||||||||
| renderedFileName := "all.patched.yaml" | ||||||||||||
| renderedFile := filepath.Join(tempDir, renderedFileName) | ||||||||||||
| r.Logf("Generating %s", renderedFile) | ||||||||||||
| r.Logf("Generating %s", renderedFileName) | ||||||||||||
|
|
||||||||||||
| kustomizeArgs := []string{"build", tempDir, "--output", renderedFile} | ||||||||||||
| kustomizeArgs := []string{"--output", renderedFile} | ||||||||||||
|
|
||||||||||||
| if !r.isUsingKubectlKustomize() { | ||||||||||||
| kustomizeArgs = append([]string{"build", tempDir}, kustomizeArgs...) | ||||||||||||
|
||||||||||||
| kustomizeArgs = append([]string{"build", tempDir}, kustomizeArgs...) | |
| kustomizeArgs = append([]string{"build", tempDir}, kustomizeArgs...) | |
| } else { | |
| // When using kubectl kustomize, explicitly pass tempDir as the target directory. | |
| kustomizeArgs = append([]string{tempDir}, kustomizeArgs...) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,6 +108,15 @@ func (r *Runner) kustomizeBin() string { | |
| if r.KustomizeBinary != "" { | ||
| return r.KustomizeBinary | ||
| } | ||
| if env := os.Getenv("KUSTOMIZE_BIN"); env != "" { | ||
| return env | ||
| } | ||
| if _, err := exec.LookPath("kustomize"); err == nil { | ||
| return "kustomize" | ||
| } | ||
| if _, err := exec.LookPath("kubectl"); err == nil { | ||
| return "kubectl kustomize" | ||
| } | ||
| return "kustomize" | ||
| } | ||
|
|
||
|
|
@@ -140,7 +149,7 @@ func (r *Runner) runBytes(envs map[string]string, dir, cmd string, args ...strin | |
|
|
||
| name := nameArgs[0] | ||
|
|
||
| if len(nameArgs) > 2 { | ||
| if len(nameArgs) > 1 { | ||
| a := append([]string{}, nameArgs[1:]...) | ||
| a = append(a, args...) | ||
|
|
||
|
|
@@ -154,10 +163,10 @@ func (r *Runner) runBytes(envs map[string]string, dir, cmd string, args ...strin | |
| wrappedErr := fmt.Errorf(`%w | ||
|
|
||
| COMMAND: | ||
| %s | ||
| %s | ||
|
|
||
| OUTPUT: | ||
| %s`, | ||
| %s`, | ||
|
Comment on lines
163
to
+169
|
||
| err, | ||
| indent(c, " "), | ||
| indent(string(errBytes), " "), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,12 @@ | ||
| package chartify | ||
|
|
||
| import ( | ||
| "os" | ||
| "path/filepath" | ||
| "testing" | ||
|
|
||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestCreateFlagChain(t *testing.T) { | ||
|
|
@@ -107,3 +110,91 @@ func TestFindSemVerInfo(t *testing.T) { | |
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestKustomizeBin(t *testing.T) { | ||
| t.Run("KustomizeBinary option is set", func(t *testing.T) { | ||
| r := New(KustomizeBin("/custom/kustomize")) | ||
| got := r.kustomizeBin() | ||
| want := "/custom/kustomize" | ||
| if got != want { | ||
| t.Errorf("kustomizeBin() = %v, want %v", got, want) | ||
| } | ||
| }) | ||
|
|
||
| t.Run("KUSTOMIZE_BIN environment variable", func(t *testing.T) { | ||
| if _, ok := os.LookupEnv("KUSTOMIZE_BIN"); ok { | ||
| t.Skip("KUSTOMIZE_BIN environment variable is already set") | ||
| } | ||
| os.Setenv("KUSTOMIZE_BIN", "/custom/kustomize") | ||
| defer os.Unsetenv("KUSTOMIZE_BIN") | ||
| r := New() | ||
| got := r.kustomizeBin() | ||
|
Comment on lines
+124
to
+131
|
||
| want := "/custom/kustomize" | ||
| if got != want { | ||
| t.Errorf("kustomizeBin() = %v, want %v", got, want) | ||
| } | ||
| }) | ||
|
|
||
| t.Run("fallback to kubectl kustomize when kustomize not found", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| binDir := filepath.Join(tmpDir, "bin") | ||
| require.NoError(t, os.MkdirAll(binDir, 0755)) | ||
|
|
||
| kubectlPath := filepath.Join(binDir, "kubectl") | ||
| kubectlContent := []byte("#!/bin/sh\necho 'kubectl version'\n") | ||
| require.NoError(t, os.WriteFile(kubectlPath, kubectlContent, 0755)) | ||
|
|
||
| origPath := os.Getenv("PATH") | ||
| defer os.Setenv("PATH", origPath) | ||
| os.Setenv("PATH", binDir) | ||
|
|
||
| r := New() | ||
| got := r.kustomizeBin() | ||
| want := "kubectl kustomize" | ||
| if got != want { | ||
| t.Errorf("kustomizeBin() = %v, want %v", got, want) | ||
| } | ||
| }) | ||
|
|
||
| t.Run("use kustomize when both kustomize and kubectl exist in PATH", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| binDir := filepath.Join(tmpDir, "bin") | ||
| require.NoError(t, os.MkdirAll(binDir, 0755)) | ||
|
|
||
| kustomizePath := filepath.Join(binDir, "kustomize") | ||
| kustomizeContent := []byte("#!/bin/sh\necho 'kustomize version'\n") | ||
| require.NoError(t, os.WriteFile(kustomizePath, kustomizeContent, 0755)) | ||
|
|
||
| kubectlPath := filepath.Join(binDir, "kubectl") | ||
| kubectlContent := []byte("#!/bin/sh\necho 'kubectl version'\n") | ||
| require.NoError(t, os.WriteFile(kubectlPath, kubectlContent, 0755)) | ||
|
|
||
| origPath := os.Getenv("PATH") | ||
| defer os.Setenv("PATH", origPath) | ||
| os.Setenv("PATH", binDir) | ||
|
|
||
| r := New() | ||
| got := r.kustomizeBin() | ||
| want := "kustomize" | ||
| if got != want { | ||
| t.Errorf("kustomizeBin() = %v, want %v", got, want) | ||
| } | ||
| }) | ||
|
|
||
| t.Run("return kustomize as fallback when neither kustomize nor kubectl exist", func(t *testing.T) { | ||
| tmpDir := t.TempDir() | ||
| binDir := filepath.Join(tmpDir, "bin") | ||
| require.NoError(t, os.MkdirAll(binDir, 0755)) | ||
|
|
||
| origPath := os.Getenv("PATH") | ||
| defer os.Setenv("PATH", origPath) | ||
| os.Setenv("PATH", binDir) | ||
|
|
||
| r := New() | ||
| got := r.kustomizeBin() | ||
| want := "kustomize" | ||
| if got != want { | ||
| t.Errorf("kustomizeBin() = %v, want %v", got, want) | ||
| } | ||
| }) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test will be skipped on systems without
kubectlin PATH. The repo CI workflow installs kustomize but not kubectl (.github/workflows/go.yml), so this coverage is likely skipped in CI and won’t protect the new behavior. Consider stubbing a minimalkubectlscript in a temp PATH (similar to TestKustomizeBin) so the test always exercises thekubectl kustomizecode path.