Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions kubectl_kustomize_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package chartify

import (
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"
)

func TestKubectlKustomizeFallback(t *testing.T) {
t.Run("KustomizeBuild with kubectl kustomize", 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'")
})
}
29 changes: 28 additions & 1 deletion kustomize.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ func (r *Runner) KustomizeBuild(srcDir string, tempDir string, opts ...Kustomize
}

if len(kustomizeOpts.Images) > 0 {
if r.isUsingKubectlKustomize() {
return "", fmt.Errorf("setting images via kustomizeOpts.Images is not supported when using 'kubectl kustomize'. Please set images directly in your kustomization.yaml file")
}
args := []string{"edit", "set", "image"}
for _, image := range kustomizeOpts.Images {
args = append(args, image.String())
Expand All @@ -120,27 +123,40 @@ func (r *Runner) KustomizeBuild(srcDir string, tempDir string, opts ...Kustomize
}
}
if kustomizeOpts.NamePrefix != "" {
if r.isUsingKubectlKustomize() {
return "", fmt.Errorf("setting namePrefix via kustomizeOpts.NamePrefix is not supported when using 'kubectl kustomize'. Please set namePrefix directly in your kustomization.yaml file")
}
_, err := r.runInDir(tempDir, r.kustomizeBin(), "edit", "set", "nameprefix", kustomizeOpts.NamePrefix)
if err != nil {
fmt.Println(err)
return "", err
}
}
if kustomizeOpts.NameSuffix != "" {
if r.isUsingKubectlKustomize() {
return "", fmt.Errorf("setting nameSuffix via kustomizeOpts.NameSuffix is not supported when using 'kubectl kustomize'. Please set nameSuffix directly in your kustomization.yaml file")
}
// "--" is there to avoid `namesuffix -acme` to fail due to `-a` being considered as a flag
_, err := r.runInDir(tempDir, r.kustomizeBin(), "edit", "set", "namesuffix", "--", kustomizeOpts.NameSuffix)
if err != nil {
return "", err
}
}
if kustomizeOpts.Namespace != "" {
if r.isUsingKubectlKustomize() {
return "", fmt.Errorf("setting namespace via kustomizeOpts.Namespace is not supported when using 'kubectl kustomize'. Please set namespace directly in your kustomization.yaml file")
}
_, err := r.runInDir(tempDir, r.kustomizeBin(), "edit", "set", "namespace", kustomizeOpts.Namespace)
if err != nil {
return "", err
}
}
outputFile := filepath.Join(tempDir, "templates", "kustomized.yaml")
kustomizeArgs := []string{"-o", outputFile, "build"}
kustomizeArgs := []string{"-o", outputFile}

if !r.isUsingKubectlKustomize() {
kustomizeArgs = append(kustomizeArgs, "build")
}

if u.EnableAlphaPlugins {
f, err := r.kustomizeEnableAlphaPluginsFlag()
Expand Down Expand Up @@ -190,10 +206,18 @@ func (r *Runner) kustomizeVersion() (*semver.Version, error) {
return version, nil
}

// isUsingKubectlKustomize checks if we're using kubectl's built-in kustomize
func (r *Runner) isUsingKubectlKustomize() bool {
return r.kustomizeBin() == "kubectl kustomize"
}

// kustomizeEnableAlphaPluginsFlag returns the kustomize binary alpha plugin argument.
// Above Kustomize v3, it is `--enable-alpha-plugins`.
// Below Kustomize v3 (including v3), it is `--enable_alpha_plugins`.
func (r *Runner) kustomizeEnableAlphaPluginsFlag() (string, error) {
if r.isUsingKubectlKustomize() {
return "--enable-alpha-plugins", nil
}
version, err := r.kustomizeVersion()
if err != nil {
return "", err
Expand All @@ -209,6 +233,9 @@ func (r *Runner) kustomizeEnableAlphaPluginsFlag() (string, error) {
// Above Kustomize v3, it is `--load-restrictor=LoadRestrictionsNone`.
// Below Kustomize v3 (including v3), it is `--load_restrictor=none`.
func (r *Runner) kustomizeLoadRestrictionsNoneFlag() (string, error) {
if r.isUsingKubectlKustomize() {
return "--load-restrictor=LoadRestrictionsNone", nil
}
version, err := r.kustomizeVersion()
if err != nil {
return "", err
Expand Down
8 changes: 6 additions & 2 deletions patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
}

if u.EnableAlphaPlugins {
f, err := r.kustomizeEnableAlphaPluginsFlag()
Expand Down
15 changes: 12 additions & 3 deletions runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

Expand Down Expand Up @@ -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...)

Expand All @@ -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`,
err,
indent(c, " "),
indent(string(errBytes), " "),
Expand Down
91 changes: 91 additions & 0 deletions util_test.go
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) {
Expand Down Expand Up @@ -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()
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)
}
})
}