mirror of
https://github.com/helmfile/helmfile.git
synced 2026-09-30 23:44:43 +02:00
In nix/devbox environments, helm plugin directories are typically symlinks into the Nix store. GetPluginVersion used entry.IsDir() which does not follow symlinks, causing the plugin to be reported as not installed. Follow symlinks with os.Stat before skipping non-directory entries. Signed-off-by: Shane Starcher <shane.starcher@gmail.com> Co-authored-by: Shane Starcher <shane.starcher@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1776 lines
57 KiB
Go
1776 lines
57 KiB
Go
package helmexec
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Masterminds/semver/v3"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/tj/assert"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Mocking the command-line runner
|
|
|
|
type mockRunner struct {
|
|
output []byte
|
|
versionOutput []byte // if set, returned for "helm version --short" probe; overrides default Helm 4 fallback
|
|
err error
|
|
}
|
|
|
|
func (mock *mockRunner) ExecuteStdIn(cmd string, args []string, env map[string]string, stdin io.Reader) ([]byte, error) {
|
|
return mock.output, mock.err
|
|
}
|
|
|
|
func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error) {
|
|
if strings.Join(args, " ") == "version --short" {
|
|
if mock.versionOutput != nil {
|
|
return mock.versionOutput, nil
|
|
}
|
|
if len(mock.output) == 0 {
|
|
return []byte("v4.0.1+g12500dd"), nil
|
|
}
|
|
}
|
|
return mock.output, mock.err
|
|
}
|
|
|
|
func MockExecer(logger *zap.SugaredLogger, kubeconfig, kubeContext string) (*execer, error) {
|
|
execer, err := New("helm", HelmExecOptions{}, logger, kubeconfig, kubeContext, &mockRunner{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return execer, nil
|
|
}
|
|
|
|
// Test methods
|
|
|
|
func TestNewHelmExec(t *testing.T) {
|
|
buffer := bytes.NewBufferString("something")
|
|
helm, err := MockExecer(NewLogger(buffer, "debug"), "config", "dev")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if helm.kubeContext != "dev" {
|
|
t.Error("helmexec.New() - kubeContext")
|
|
}
|
|
if buffer.String() != "something" {
|
|
t.Error("helmexec.New() - changed buffer")
|
|
}
|
|
if len(helm.extra) != 0 {
|
|
t.Error("helmexec.New() - extra args not empty")
|
|
}
|
|
}
|
|
|
|
func Test_SetExtraArgs(t *testing.T) {
|
|
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
helm.SetExtraArgs()
|
|
if len(helm.extra) != 0 {
|
|
t.Error("helmexec.SetExtraArgs() - passing no arguments should not change extra field")
|
|
}
|
|
helm.SetExtraArgs("foo")
|
|
if !reflect.DeepEqual(helm.extra, []string{"foo"}) {
|
|
t.Error("helmexec.SetExtraArgs() - one extra argument missing")
|
|
}
|
|
helm.SetExtraArgs("alpha", "beta")
|
|
if !reflect.DeepEqual(helm.extra, []string{"alpha", "beta"}) {
|
|
t.Error("helmexec.SetExtraArgs() - two extra arguments missing (overwriting the previous value)")
|
|
}
|
|
}
|
|
|
|
func Test_SetHelmBinary(t *testing.T) {
|
|
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if helm.helmBinary != "helm" {
|
|
t.Error("helmexec.command - default command is not helm")
|
|
}
|
|
helm.SetHelmBinary("foo")
|
|
if helm.helmBinary != "foo" {
|
|
t.Errorf("helmexec.SetHelmBinary() - actual = %s expect = foo", helm.helmBinary)
|
|
}
|
|
}
|
|
|
|
func Test_SetEnableLiveOutput(t *testing.T) {
|
|
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if helm.options.EnableLiveOutput {
|
|
t.Error("helmexec.options.EnableLiveOutput should not be enabled by default")
|
|
}
|
|
helm.SetEnableLiveOutput(true)
|
|
if !helm.options.EnableLiveOutput {
|
|
t.Errorf("helmexec.SetEnableLiveOutput() - actual = %t expect = true", helm.options.EnableLiveOutput)
|
|
}
|
|
}
|
|
|
|
func Test_AddRepo_Helm_Version(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
version string
|
|
disableForceUpdate bool
|
|
expected string
|
|
}{
|
|
{
|
|
name: "Helm 3.2.0 (before force-update)",
|
|
version: "3.2.0",
|
|
expected: `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem
|
|
`,
|
|
},
|
|
{
|
|
name: "Helm 3.3.2 (force-update added)",
|
|
version: "3.3.2",
|
|
expected: `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem
|
|
`,
|
|
},
|
|
{
|
|
name: "Helm 3.19.2 (with force-update)",
|
|
version: "3.19.2",
|
|
expected: `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem
|
|
`,
|
|
},
|
|
{
|
|
name: "Helm 3.19.2 (force-update disabled)",
|
|
version: "3.19.2",
|
|
disableForceUpdate: true,
|
|
expected: `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem
|
|
`,
|
|
},
|
|
{
|
|
name: "Helm 4.0.1 (force-update needed)",
|
|
version: "4.0.1",
|
|
expected: `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem
|
|
`,
|
|
},
|
|
{
|
|
name: "Helm 4.0.1 (force-update disabled)",
|
|
version: "4.0.1",
|
|
disableForceUpdate: true,
|
|
expected: `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem
|
|
`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm := &execer{
|
|
helmBinary: "helm",
|
|
version: semver.MustParse(tt.version),
|
|
logger: logger,
|
|
kubeconfig: "config",
|
|
kubeContext: "dev",
|
|
runner: &mockRunner{},
|
|
options: HelmExecOptions{DisableForceUpdate: tt.disableForceUpdate},
|
|
}
|
|
err := helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "", "", false, false)
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.expected, buffer.String())
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_AddRepo(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Test case with certfile and keyfile
|
|
buffer.Reset()
|
|
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "", "", false, false)
|
|
expected := `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test case with cafile
|
|
buffer.Reset()
|
|
err = helm.AddRepo("myRepo", "https://repo.example.com/", "ca.crt", "", "", "", "", "", false, false)
|
|
expected = `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --ca-file ca.crt
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test case with no certfile or cafile
|
|
buffer.Reset()
|
|
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "", "", "", false, false)
|
|
expected = `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test case with managed "acr"
|
|
buffer.Reset()
|
|
err = helm.AddRepo("acrRepo", "", "", "", "", "", "", "acr", false, false)
|
|
expected = `Adding repo acrRepo (acr)
|
|
exec: az acr helm repo add --name acrRepo
|
|
exec: az acr helm repo add --name acrRepo:
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test case with unknown managed type
|
|
buffer.Reset()
|
|
err = helm.AddRepo("otherRepo", "", "", "", "", "", "", "unknown", false, false)
|
|
expected = `ERROR: unknown type 'unknown' for repository otherRepo
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test case with username and password (using password-stdin)
|
|
buffer.Reset()
|
|
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password", "", false, false)
|
|
expected = `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --username example_user --password-stdin
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test case with username, password, and pass-credentials
|
|
buffer.Reset()
|
|
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password", "", true, false)
|
|
expected = `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --pass-credentials --username example_user --password-stdin
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
actual := buffer.String()
|
|
if actual != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
|
|
// Test case with skipTLSVerify
|
|
buffer.Reset()
|
|
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "", "", "", false, true)
|
|
expected = `Adding repo myRepo https://repo.example.com/
|
|
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --force-update --insecure-skip-tls-verify
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
normalize := func(s string) string {
|
|
return strings.Join(strings.Fields(s), " ")
|
|
}
|
|
|
|
actual = normalize(buffer.String())
|
|
expected = normalize(expected)
|
|
|
|
if actual != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
|
|
// Test case with empty name
|
|
buffer.Reset()
|
|
err = helm.AddRepo("", "https://repo.example.com/", "", "", "", "", "", "", false, false)
|
|
expected = `empty field name`
|
|
|
|
if err != nil && err.Error() != "empty field name" {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
actual = strings.TrimSpace(buffer.String())
|
|
if actual != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
}
|
|
|
|
func Test_UpdateRepo(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.UpdateRepo()
|
|
expected := `Updating repo
|
|
exec: helm --kubeconfig config --kube-context dev repo update
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.UpdateRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_RegistryLogin(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm := &execer{
|
|
helmBinary: "helm",
|
|
version: semver.MustParse("v3.12.0"),
|
|
logger: logger,
|
|
kubeconfig: "config",
|
|
kubeContext: "dev",
|
|
runner: &mockRunner{},
|
|
}
|
|
err := helm.RegistryLogin("repo.example.com", "example_user", "example_password", "example_ca", "example_cert", "example_key", true)
|
|
expected := `Logging in to registry
|
|
exec: helm --kubeconfig config --kube-context dev registry login repo.example.com --cert-file example_cert --key-file example_key --ca-file example_ca --insecure --username example_user --password-stdin
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.RegistryLogin()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test helm version prior to v3.12.0, without support for TLS
|
|
buffer.Reset()
|
|
helm.version = semver.MustParse("v3.11.0")
|
|
|
|
err = helm.RegistryLogin("repo.example.com", "example_user", "example_password", "example_ca", "example_cert", "example_key", true)
|
|
expected = `Logging in to registry
|
|
exec: helm --kubeconfig config --kube-context dev registry login repo.example.com --insecure --username example_user --password-stdin
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.RegistryLogin()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_SyncRelease(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.SyncRelease(HelmContext{}, "release", "chart", "default", "--timeout 10", "--wait", "--wait-for-jobs")
|
|
expected := `Upgrading release=release, chart=chart, namespace=default
|
|
exec: helm --kubeconfig config --kube-context dev upgrade --install release chart --timeout 10 --wait --wait-for-jobs --history-max 0
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.SyncRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
err = helm.SyncRelease(HelmContext{}, "release", "chart", "default")
|
|
expected = `Upgrading release=release, chart=chart, namespace=default
|
|
exec: helm --kubeconfig config --kube-context dev upgrade --install release chart --history-max 0
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.SyncRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
err = helm.SyncRelease(HelmContext{}, "release", "https://example_user:example_password@repo.example.com/chart.tgz", "default")
|
|
expected = `Upgrading release=release, chart=https://example_user:xxxxx@repo.example.com/chart.tgz, namespace=default
|
|
exec: helm --kubeconfig config --kube-context dev upgrade --install release https://example_user:example_password@repo.example.com/chart.tgz --history-max 0
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.SyncRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_UpdateDeps(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.UpdateDeps("./chart/foo")
|
|
expected := `Updating dependency ./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency update ./chart/foo
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.UpdateDeps()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
helm.SetExtraArgs("--verify")
|
|
err = helm.UpdateDeps("./chart/foo")
|
|
// --verify is a dependency-specific flag and should be preserved
|
|
expected = `Updating dependency ./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency update ./chart/foo --verify
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.UpdateDeps()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_BuildDeps(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm3Runner := mockRunner{output: []byte("v3.2.4+ge29ce2a")}
|
|
helm, err := New("helm", HelmExecOptions{}, logger, "config", "dev", &helm3Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.BuildDeps("foo", "./chart/foo", []string{"--skip-refresh"}...)
|
|
expected := `Building dependency release=foo, chart=./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo --skip-refresh
|
|
v3.2.4+ge29ce2a
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.BuildDeps()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
err = helm.BuildDeps("foo", "./chart/foo")
|
|
expected = `Building dependency release=foo, chart=./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo
|
|
v3.2.4+ge29ce2a
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.BuildDeps()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
helm.SetExtraArgs("--verify")
|
|
err = helm.BuildDeps("foo", "./chart/foo", []string{"--skip-refresh"}...)
|
|
// --verify is a dependency-specific flag and should be preserved
|
|
expected = `Building dependency release=foo, chart=./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo --skip-refresh --verify
|
|
v3.2.4+ge29ce2a
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.BuildDeps()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94")}
|
|
helm, err = New("helm", HelmExecOptions{}, logger, "config", "dev", &helm2Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.BuildDeps("foo", "./chart/foo")
|
|
expected = `Building dependency release=foo, chart=./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo
|
|
Client: v2.16.1+ge13bc94
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.BuildDeps()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test that --dry-run flag is filtered out (not supported by helm dependency build)
|
|
buffer.Reset()
|
|
helm3Runner = mockRunner{output: []byte("v3.2.4+ge29ce2a")}
|
|
helm, err = New("helm", HelmExecOptions{}, logger, "config", "dev", &helm3Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
helm.SetExtraArgs("--dry-run=server")
|
|
err = helm.BuildDeps("foo", "./chart/foo")
|
|
expected = `Building dependency release=foo, chart=./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo
|
|
v3.2.4+ge29ce2a
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.BuildDeps() with --dry-run should filter it out\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
// Test that global flags (--debug) and dependency flags (--verify) are preserved,
|
|
// while template-specific flags (--dry-run) are filtered out
|
|
buffer.Reset()
|
|
helm3Runner = mockRunner{output: []byte("v3.2.4+ge29ce2a")}
|
|
helm, err = New("helm", HelmExecOptions{}, logger, "config", "dev", &helm3Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
helm.SetExtraArgs("--debug", "--dry-run=server", "--verify", "--wait")
|
|
err = helm.BuildDeps("foo", "./chart/foo")
|
|
expected = `Building dependency release=foo, chart=./chart/foo
|
|
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo --debug --verify
|
|
v3.2.4+ge29ce2a
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.BuildDeps() should preserve global and dependency flags\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_DecryptSecret(t *testing.T) {
|
|
// Set secrets plugin version to 4.7.4
|
|
if err := os.Setenv("HELM_PLUGINS", "../../test/plugins/secrets/4.7.4"); err != nil {
|
|
t.Errorf("failed to set environment HELM_PLUGINS error: %s", err)
|
|
}
|
|
defer func() {
|
|
if err := os.Unsetenv("HELM_PLUGINS"); err != nil {
|
|
t.Errorf("failed to unset environment HELM_PLUGINS error: %s", err)
|
|
}
|
|
}()
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
tmpFilePath := "path/to/temp/file"
|
|
helm.writeTempFile = func(content []byte) (string, error) {
|
|
return tmpFilePath, nil
|
|
}
|
|
|
|
_, err = helm.DecryptSecret(HelmContext{}, "secretName")
|
|
if err != nil {
|
|
t.Errorf("Error: %v", err)
|
|
}
|
|
cwd, err := filepath.Abs(".")
|
|
if err != nil {
|
|
t.Errorf("Error: %v", err)
|
|
}
|
|
// Run again for caching
|
|
_, err = helm.DecryptSecret(HelmContext{}, "secretName")
|
|
|
|
expected := fmt.Sprintf(`Preparing to decrypt secret %v/secretName
|
|
Decrypting secret %s/secretName
|
|
exec: helm --kubeconfig config --kube-context dev secrets decrypt %s/secretName
|
|
Decrypted %s/secretName into %s
|
|
Preparing to decrypt secret %s/secretName
|
|
Found secret in cache %s/secretName
|
|
Decrypted %s/secretName into %s
|
|
`, cwd, cwd, cwd, cwd, tmpFilePath, cwd, cwd, cwd, tmpFilePath)
|
|
if err != nil {
|
|
if _, ok := err.(*os.PathError); ok {
|
|
} else {
|
|
t.Errorf("Error: %v", err)
|
|
}
|
|
}
|
|
if d := cmp.Diff(expected, buffer.String()); d != "" {
|
|
t.Errorf("helmexec.DecryptSecret(): want (-), got (+):\n%s", d)
|
|
}
|
|
}
|
|
|
|
func Test_DecryptSecretWithGotmpl(t *testing.T) {
|
|
// Set secrets plugin version to 4.7.4
|
|
if err := os.Setenv("HELM_PLUGINS", "../../test/plugins/secrets/4.7.4"); err != nil {
|
|
t.Errorf("failed to set environment HELM_PLUGINS error: %s", err)
|
|
}
|
|
defer func() {
|
|
if err := os.Unsetenv("HELM_PLUGINS"); err != nil {
|
|
t.Errorf("failed to unset environment HELM_PLUGINS error: %s", err)
|
|
}
|
|
}()
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
tmpFilePath := "path/to/temp/file"
|
|
helm.writeTempFile = func(content []byte) (string, error) {
|
|
return tmpFilePath, nil
|
|
}
|
|
|
|
secretName := "secretName.yaml.gotmpl"
|
|
_, err = helm.DecryptSecret(HelmContext{}, secretName)
|
|
if err != nil {
|
|
t.Errorf("Error: %v", err)
|
|
}
|
|
cwd, err := filepath.Abs(".")
|
|
if err != nil {
|
|
t.Errorf("Error: %v", err)
|
|
}
|
|
|
|
expected := fmt.Sprintf(`Preparing to decrypt secret %v/secretName.yaml.gotmpl
|
|
Decrypting secret %s/secretName.yaml.gotmpl
|
|
exec: helm --kubeconfig config --kube-context dev secrets decrypt %s/secretName.yaml.gotmpl
|
|
Decrypted %s/secretName.yaml.gotmpl into %s
|
|
`, cwd, cwd, cwd, cwd, tmpFilePath)
|
|
if err != nil {
|
|
t.Errorf("Error: %v", err)
|
|
}
|
|
if d := cmp.Diff(expected, buffer.String()); d != "" {
|
|
t.Errorf("helmexec.DecryptSecret(): want (-), got (+):\n%s", d)
|
|
}
|
|
}
|
|
|
|
func Test_DiffRelease(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.DiffRelease(HelmContext{}, "release", "chart", "default", false, "--timeout 10", "--wait", "--wait-for-jobs")
|
|
expected := `Comparing release=release, chart=chart, namespace=default
|
|
|
|
exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unreleased release chart --timeout 10 --wait --wait-for-jobs
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.DiffRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
err = helm.DiffRelease(HelmContext{}, "release", "chart", "default", false)
|
|
expected = `Comparing release=release, chart=chart, namespace=default
|
|
|
|
exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unreleased release chart
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.DiffRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
err = helm.DiffRelease(HelmContext{}, "release", "https://example_user:example_password@repo.example.com/chart.tgz", "default", false)
|
|
expected = `Comparing release=release, chart=https://example_user:xxxxx@repo.example.com/chart.tgz, namespace=default
|
|
|
|
exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unreleased release https://example_user:example_password@repo.example.com/chart.tgz
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.DiffRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_DiffRelease_ColorFlagHelm4(t *testing.T) {
|
|
// Test that --color and --no-color flags are removed and HELM_DIFF_COLOR env var is set for Helm 4
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
|
|
// MockExecer creates a Helm 4 execer by default (returns v4.0.1)
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Verify it's Helm 4
|
|
if !helm.IsHelm4() {
|
|
t.Errorf("expected Helm 4, got version: %v", helm.GetVersion())
|
|
}
|
|
|
|
// Test with --color flag
|
|
buffer.Reset()
|
|
err = helm.DiffRelease(HelmContext{}, "release", "chart", "default", false, "--color", "--context", "3")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
// The --color flag should be removed and --context should remain
|
|
expected := `Comparing release=release, chart=chart, namespace=default
|
|
|
|
exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unreleased release chart --context 3
|
|
`
|
|
actual := buffer.String()
|
|
if actual != expected {
|
|
t.Errorf("helmexec.DiffRelease() with --color\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
|
|
// Verify --color flag was removed
|
|
if strings.Contains(actual, "--color") {
|
|
t.Errorf("--color flag should have been removed, but got: %v", actual)
|
|
}
|
|
|
|
// Test with --no-color flag
|
|
buffer.Reset()
|
|
err = helm.DiffRelease(HelmContext{}, "release", "chart", "default", false, "--no-color", "--context", "3")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
// The --no-color flag should be removed and --context should remain
|
|
expected = `Comparing release=release, chart=chart, namespace=default
|
|
|
|
exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unreleased release chart --context 3
|
|
`
|
|
actual = buffer.String()
|
|
if actual != expected {
|
|
t.Errorf("helmexec.DiffRelease() with --no-color\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
|
|
// Verify --no-color flag was removed
|
|
if strings.Contains(actual, "--no-color") {
|
|
t.Errorf("--no-color flag should have been removed, but got: %v", actual)
|
|
}
|
|
}
|
|
|
|
func Test_FilterColorFlagsForHelm4(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
inputFlags []string
|
|
expectedFlags []string
|
|
expectedEnvKey string
|
|
expectedEnvVal string
|
|
}{
|
|
{
|
|
name: "color flag",
|
|
inputFlags: []string{"--color", "--context", "3"},
|
|
expectedFlags: []string{"--context", "3"},
|
|
expectedEnvKey: "HELM_DIFF_COLOR",
|
|
expectedEnvVal: "true",
|
|
},
|
|
{
|
|
name: "no-color flag",
|
|
inputFlags: []string{"--no-color", "--context", "3"},
|
|
expectedFlags: []string{"--context", "3"},
|
|
expectedEnvKey: "HELM_DIFF_COLOR",
|
|
expectedEnvVal: "false",
|
|
},
|
|
{
|
|
name: "no color flags",
|
|
inputFlags: []string{"--context", "3", "--detailed-exitcode"},
|
|
expectedFlags: []string{"--context", "3", "--detailed-exitcode"},
|
|
expectedEnvKey: "",
|
|
expectedEnvVal: "",
|
|
},
|
|
{
|
|
name: "color flag with other flags",
|
|
inputFlags: []string{"--detailed-exitcode", "--color", "--suppress", "secret"},
|
|
expectedFlags: []string{"--detailed-exitcode", "--suppress", "secret"},
|
|
expectedEnvKey: "HELM_DIFF_COLOR",
|
|
expectedEnvVal: "true",
|
|
},
|
|
{
|
|
name: "both color and no-color flags (first wins with defensive check)",
|
|
inputFlags: []string{"--color", "--no-color", "--context", "3"},
|
|
expectedFlags: []string{"--context", "3"},
|
|
expectedEnvKey: "HELM_DIFF_COLOR",
|
|
expectedEnvVal: "true", // Changed: first flag wins due to defensive check
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
env := make(map[string]string)
|
|
actualFlags := helm.filterColorFlagsForHelm4(tt.inputFlags, env)
|
|
|
|
if !reflect.DeepEqual(actualFlags, tt.expectedFlags) {
|
|
t.Errorf("filterColorFlagsForHelm4() flags\nactual = %v\nexpect = %v", actualFlags, tt.expectedFlags)
|
|
}
|
|
|
|
if tt.expectedEnvKey != "" {
|
|
if env[tt.expectedEnvKey] != tt.expectedEnvVal {
|
|
t.Errorf("filterColorFlagsForHelm4() env[%v]\nactual = %v\nexpect = %v",
|
|
tt.expectedEnvKey, env[tt.expectedEnvKey], tt.expectedEnvVal)
|
|
}
|
|
} else if len(env) > 0 {
|
|
t.Errorf("filterColorFlagsForHelm4() expected no env vars, but got: %v", env)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_DeleteRelease(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.DeleteRelease(HelmContext{}, "release")
|
|
expected := `Deleting release
|
|
exec: helm --kubeconfig config --kube-context dev delete release
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.DeleteRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
func Test_DeleteRelease_Flags(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.DeleteRelease(HelmContext{}, "release", "--purge")
|
|
expected := `Deleting release
|
|
exec: helm --kubeconfig config --kube-context dev delete release --purge
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.DeleteRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_TestRelease(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.TestRelease(HelmContext{}, "release")
|
|
expected := `Testing release
|
|
exec: helm --kubeconfig config --kube-context dev test release
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.TestRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
func Test_TestRelease_Flags(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.TestRelease(HelmContext{}, "release", "--cleanup", "--timeout", "60")
|
|
expected := `Testing release
|
|
exec: helm --kubeconfig config --kube-context dev test release --cleanup --timeout 60
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.TestRelease()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_ReleaseStatus(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.ReleaseStatus(HelmContext{}, "myRelease")
|
|
expected := `Getting status myRelease
|
|
exec: helm --kubeconfig config --kube-context dev status myRelease
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.ReleaseStatus()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_exec(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "", "")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
env := map[string]string{}
|
|
_, err = helm.exec([]string{"version"}, env, nil)
|
|
expected := `exec: helm version
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.exec()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
helm, err = MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
ret, _ := helm.exec([]string{"diff"}, env, nil)
|
|
if len(ret) != 0 {
|
|
t.Error("helmexec.exec() - expected empty return value")
|
|
}
|
|
|
|
buffer.Reset()
|
|
helm, err = MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
_, err = helm.exec([]string{"diff", "release", "chart", "--timeout 10", "--wait", "--wait-for-jobs"}, env, nil)
|
|
expected = `exec: helm --kubeconfig config --kube-context dev diff release chart --timeout 10 --wait --wait-for-jobs
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.exec()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
_, err = helm.exec([]string{"version"}, env, nil)
|
|
expected = `exec: helm --kubeconfig config --kube-context dev version
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.exec()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
helm.SetExtraArgs("foo")
|
|
_, err = helm.exec([]string{"version"}, env, nil)
|
|
expected = `exec: helm --kubeconfig config --kube-context dev version foo
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.exec()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
helm, err = MockExecer(logger, "", "")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
helm.SetHelmBinary("overwritten")
|
|
_, err = helm.exec([]string{"version"}, env, nil)
|
|
expected = `exec: overwritten version
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.exec()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_Lint(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.Lint("release", "path/to/chart", "--values", "file.yml")
|
|
expected := `Linting release=release, chart=path/to/chart
|
|
exec: helm --kubeconfig config --kube-context dev lint path/to/chart --values file.yml
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.Lint()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_Fetch(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.Fetch("chart", "--version", "1.2.3", "--untar", "--untardir", "/tmp/dir")
|
|
expected := `Fetching chart
|
|
exec: helm --kubeconfig config --kube-context dev fetch chart --version 1.2.3 --untar --untardir /tmp/dir
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.Fetch()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
err = helm.Fetch("https://example_user:example_password@repo.example.com/chart.tgz", "--version", "1.2.3", "--untar", "--untardir", "/tmp/dir")
|
|
expected = `Fetching https://example_user:xxxxx@repo.example.com/chart.tgz
|
|
exec: helm --kubeconfig config --kube-context dev fetch https://example_user:example_password@repo.example.com/chart.tgz --version 1.2.3 --untar --untardir /tmp/dir
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.Fetch()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_ChartPull(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
tests := []struct {
|
|
name string
|
|
helmBin string
|
|
helmVersion string
|
|
chartName string
|
|
chartPath string
|
|
chartFlags []string
|
|
listResult string
|
|
}{
|
|
{
|
|
name: "less then v3.7.0",
|
|
helmBin: "helm",
|
|
helmVersion: "v3.6.0",
|
|
chartName: "chart",
|
|
chartPath: "path1",
|
|
chartFlags: []string{"--untar", "--untardir", "/tmp/dir"},
|
|
listResult: `Pulling chart
|
|
exec: helm --kubeconfig config --kube-context dev chart pull chart
|
|
`,
|
|
},
|
|
{
|
|
name: "more then v3.7.0",
|
|
helmBin: "helm",
|
|
helmVersion: "v3.10.0",
|
|
chartName: "repo/helm-charts:0.14.0",
|
|
chartPath: "path1",
|
|
chartFlags: []string{"--untardir", "/tmp/dir", "--version", "0.14.0"},
|
|
listResult: `Pulling repo/helm-charts:0.14.0
|
|
exec: helm --kubeconfig config --kube-context dev pull oci://repo/helm-charts --destination path1 --untar --untardir /tmp/dir --version 0.14.0
|
|
`,
|
|
},
|
|
{
|
|
name: "more then v3.7.0 with rc",
|
|
helmBin: "helm",
|
|
helmVersion: "v3.14.0-rc.1+g69dcc92",
|
|
chartName: "repo/helm-charts",
|
|
chartPath: "path1",
|
|
chartFlags: []string{"--untardir", "/tmp/dir", "--devel"},
|
|
listResult: `Pulling repo/helm-charts
|
|
exec: helm --kubeconfig config --kube-context dev pull oci://repo/helm-charts --destination path1 --untar --untardir /tmp/dir --devel
|
|
`,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
tt := tests[i]
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
buffer.Reset()
|
|
helm, err := New(tt.helmBin, HelmExecOptions{}, logger, "config", "dev", &mockRunner{output: []byte(tt.helmVersion)})
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.ChartPull(tt.chartName, tt.chartPath, tt.chartFlags...)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
isMatch, _ := regexp.MatchString(tt.listResult, buffer.String())
|
|
if !isMatch {
|
|
t.Errorf("helmexec.ChartPull()\nactual = %v\nexpect = %v", buffer.String(), tt.listResult)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_ChartExport(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
tests := []struct {
|
|
name string
|
|
helmBin string
|
|
helmVersion string
|
|
chartName string
|
|
chartPath string
|
|
listResult string
|
|
expectedError string
|
|
}{
|
|
{
|
|
name: "",
|
|
helmBin: "helm",
|
|
helmVersion: "v3.6.0",
|
|
chartName: "chart",
|
|
chartPath: "path1",
|
|
listResult: `Exporting chart
|
|
exec: helm --kubeconfig config --kube-context dev chart export chart --destination path1
|
|
`,
|
|
expectedError: "",
|
|
},
|
|
}
|
|
for i := range tests {
|
|
tt := tests[i]
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
buffer.Reset()
|
|
helm := &execer{
|
|
helmBinary: tt.helmBin,
|
|
version: semver.MustParse(tt.helmVersion),
|
|
logger: logger,
|
|
kubeconfig: "config",
|
|
kubeContext: "dev",
|
|
runner: &mockRunner{},
|
|
}
|
|
err := helm.ChartExport(tt.chartName, tt.chartPath)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != tt.listResult {
|
|
t.Errorf("helmexec.ChartExport()\nactual = %v\nexpect = %v", buffer.String(), tt.listResult)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var logLevelTests = map[string]string{
|
|
"debug": `Adding repo myRepo https://repo.example.com/
|
|
exec: helm repo add myRepo https://repo.example.com/ --force-update --username example_user --password-stdin
|
|
`,
|
|
"info": `Adding repo myRepo https://repo.example.com/
|
|
`,
|
|
"warn": ``,
|
|
}
|
|
|
|
func Test_LogLevels(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
for logLevel, expected := range logLevelTests {
|
|
buffer.Reset()
|
|
logger := NewLogger(&buffer, logLevel)
|
|
helm, err := MockExecer(logger, "", "")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password", "", false, false)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
actual := buffer.String()
|
|
|
|
if strings.Contains(actual, "--password-stdin") {
|
|
expected = strings.Replace(expected, "--password example_password", "--password-stdin", 1)
|
|
}
|
|
|
|
if actual != expected {
|
|
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_mergeEnv(t *testing.T) {
|
|
actual := env2map(mergeEnv([]string{"A=1", "B=c=d", "E=2"}, map[string]string{"B": "3", "F": "4"}))
|
|
expected := map[string]string{"A": "1", "B": "3", "E": "2", "F": "4"}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Errorf("mergeEnv()\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
}
|
|
|
|
func Test_Template(t *testing.T) {
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm, err := MockExecer(logger, "config", "dev")
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
err = helm.TemplateRelease("release", "path/to/chart", "--values", "file.yml")
|
|
expected := `Templating release=release, chart=path/to/chart
|
|
exec: helm --kubeconfig config --kube-context dev template release path/to/chart --values file.yml
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.Template()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
|
|
buffer.Reset()
|
|
err = helm.TemplateRelease("release", "https://example_user:example_password@repo.example.com/chart.tgz", "--values", "file.yml")
|
|
expected = `Templating release=release, chart=https://example_user:xxxxx@repo.example.com/chart.tgz
|
|
exec: helm --kubeconfig config --kube-context dev template release https://example_user:example_password@repo.example.com/chart.tgz --values file.yml
|
|
`
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if buffer.String() != expected {
|
|
t.Errorf("helmexec.Template()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
|
}
|
|
}
|
|
|
|
func Test_Template_PostRendererWithOutputDir(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
postRendererFlag string
|
|
}{
|
|
{"separate flags", "--post-renderer"},
|
|
{"combined flag", "--post-renderer=/bin/echo"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
|
|
// Use Helm 3 version for the version probe so the Helm 3 workaround is applied.
|
|
// The workaround is not needed for Helm 4, which natively applies --post-renderer to --output-dir output.
|
|
runner := &mockRunner{versionOutput: []byte("v3.20.0")}
|
|
helm, err := New("helm", HelmExecOptions{}, logger, "config", "dev", runner)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
runner.output = []byte("apiVersion: v1\nkind: Namespace\n")
|
|
|
|
var flags []string
|
|
if tt.postRendererFlag == "--post-renderer" {
|
|
flags = []string{"--post-renderer", "/bin/echo", "--output-dir", tmpDir, "--values", "file.yml"}
|
|
} else {
|
|
flags = []string{tt.postRendererFlag, "--output-dir", tmpDir, "--values", "file.yml"}
|
|
}
|
|
|
|
err = helm.TemplateRelease("myrelease", "path/to/chart", flags...)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
outputPath := filepath.Join(tmpDir, "templates", "myrelease.yaml")
|
|
data, err := os.ReadFile(outputPath)
|
|
if err != nil {
|
|
t.Fatalf("expected output file %s to exist: %v", outputPath, err)
|
|
}
|
|
|
|
expected := "apiVersion: v1\nkind: Namespace\n\n"
|
|
if string(data) != expected {
|
|
t.Errorf("output file content:\nactual=%q\nexpect=%q", string(data), expected)
|
|
}
|
|
|
|
outputLog := buffer.String()
|
|
if strings.Contains(outputLog, "--output-dir") {
|
|
t.Errorf("helm should NOT have been called with --output-dir, got: %s", outputLog)
|
|
}
|
|
if !strings.Contains(outputLog, "--post-renderer") {
|
|
t.Errorf("helm should have been called with --post-renderer, got: %s", outputLog)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_IsHelm3(t *testing.T) {
|
|
helm3Runner := mockRunner{output: []byte("v3.0.0+ge29ce2a\n")}
|
|
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if !helm.IsHelm3() {
|
|
t.Error("helmexec.IsHelm3() - Failed to detect Helm 3")
|
|
}
|
|
|
|
helm4Runner := mockRunner{output: []byte("v4.0.1+g12500dd\n")}
|
|
helm, err = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm4Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if helm.IsHelm3() {
|
|
t.Error("helmexec.IsHelm3() - Detected Helm 3 with Helm 4 version")
|
|
}
|
|
}
|
|
|
|
func Test_IsHelm4(t *testing.T) {
|
|
helm3Runner := mockRunner{output: []byte("v3.0.0+ge29ce2a\n")}
|
|
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if helm.IsHelm4() {
|
|
t.Error("helmexec.IsHelm4() - Detected Helm 4 with Helm 3 version")
|
|
}
|
|
|
|
helm4Runner := mockRunner{output: []byte("v4.0.1+g12500dd\n")}
|
|
helm, err = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm4Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if !helm.IsHelm4() {
|
|
t.Error("helmexec.IsHelm4() - Failed to detect Helm 4")
|
|
}
|
|
}
|
|
|
|
func Test_GetPluginVersion(t *testing.T) {
|
|
v3ExpectedVersion := "3.15.0"
|
|
v4ExpectedVersion := "4.7.4"
|
|
v3PluginDirPath := "../../test/plugins/secrets/3.15.0"
|
|
v4PluginDirPath := "../../test/plugins/secrets/4.7.4"
|
|
|
|
v3SecretPluginVersion, err := GetPluginVersion("secrets", v3PluginDirPath)
|
|
require.NoError(t, err)
|
|
|
|
if v3SecretPluginVersion.String() != v3ExpectedVersion {
|
|
t.Errorf("secrets v3 plugin version is %v, expected %v", v3SecretPluginVersion.String(), v3ExpectedVersion)
|
|
}
|
|
|
|
v4SecretPluginVersion, err := GetPluginVersion("secrets", v4PluginDirPath)
|
|
require.NoError(t, err)
|
|
if v4SecretPluginVersion.String() != v4ExpectedVersion {
|
|
t.Errorf("secrets v4 plugin version is %v, expected %v", v4SecretPluginVersion.String(), v4ExpectedVersion)
|
|
}
|
|
}
|
|
|
|
func Test_GetPluginVersion_XDGPaths(t *testing.T) {
|
|
v3ExpectedVersion := "3.15.0"
|
|
v4ExpectedVersion := "4.7.4"
|
|
v3PluginDirPath := "../../test/plugins/secrets/3.15.0"
|
|
v4PluginDirPath := "../../test/plugins/secrets/4.7.4"
|
|
|
|
sep := string(os.PathListSeparator)
|
|
xdgPaths := "nonexistent/path" + sep + v3PluginDirPath + sep + "another/nonexistent"
|
|
|
|
pluginVersion, err := GetPluginVersion("secrets", xdgPaths)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, v3ExpectedVersion, pluginVersion.String())
|
|
|
|
xdgPathsV4 := v4PluginDirPath + sep + "nonexistent/path"
|
|
pluginVersion, err = GetPluginVersion("secrets", xdgPathsV4)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, v4ExpectedVersion, pluginVersion.String())
|
|
|
|
_, err = GetPluginVersion("nonexistent-plugin", xdgPaths)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "plugin nonexistent-plugin not installed")
|
|
}
|
|
|
|
func Test_GetPluginVersion_Symlink(t *testing.T) {
|
|
// Simulate a nix/devbox environment where the plugin directory is a symlink
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create a real plugin directory with plugin.yaml
|
|
realPluginDir := filepath.Join(tmpDir, "real", "helm-diff")
|
|
require.NoError(t, os.MkdirAll(realPluginDir, 0o755))
|
|
require.NoError(t, os.WriteFile(filepath.Join(realPluginDir, "plugin.yaml"), []byte(`name: "diff"
|
|
version: "3.12.0"
|
|
`), 0o644))
|
|
|
|
// Create a plugins directory where helm-diff is a symlink (like nix/devbox)
|
|
symlinkPluginsDir := filepath.Join(tmpDir, "plugins")
|
|
require.NoError(t, os.MkdirAll(symlinkPluginsDir, 0o755))
|
|
require.NoError(t, os.Symlink(realPluginDir, filepath.Join(symlinkPluginsDir, "helm-diff")))
|
|
|
|
version, err := GetPluginVersion("diff", symlinkPluginsDir)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "3.12.0", version.String())
|
|
}
|
|
|
|
func Test_GetVersion(t *testing.T) {
|
|
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
|
|
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
ver := helm.GetVersion()
|
|
if ver.Major != 2 || ver.Minor != 16 || ver.Patch != 1 {
|
|
t.Errorf("helmexec.GetVersion - did not detect correct Helm2 version; it was: %+v", ver)
|
|
}
|
|
|
|
helm3Runner := mockRunner{output: []byte("v3.2.4+ge29ce2a\n")}
|
|
helm, err = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
ver = helm.GetVersion()
|
|
if ver.Major != 3 || ver.Minor != 2 || ver.Patch != 4 {
|
|
t.Errorf("helmexec.GetVersion - did not detect correct Helm3 version; it was: %+v", ver)
|
|
}
|
|
}
|
|
|
|
func Test_IsVersionAtLeast(t *testing.T) {
|
|
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
|
|
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if !helm.IsVersionAtLeast("2.1.0") {
|
|
t.Error("helmexec.IsVersionAtLeast - 2.16.1 not atleast 2.1")
|
|
}
|
|
|
|
if helm.IsVersionAtLeast("2.19.0") {
|
|
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 2.19")
|
|
}
|
|
|
|
if helm.IsVersionAtLeast("3.2.0") {
|
|
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 3.2")
|
|
}
|
|
}
|
|
|
|
func Test_resolveOciChart(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
chartPath string
|
|
ociChartURL string
|
|
ociChartTag string
|
|
}{
|
|
{
|
|
name: "normal",
|
|
chartPath: "chart/nginx:v1",
|
|
ociChartURL: "oci://chart/nginx",
|
|
ociChartTag: "v1",
|
|
},
|
|
{
|
|
name: "contains the port",
|
|
chartPath: "chart:5000/nginx:v1",
|
|
ociChartURL: "oci://chart:5000/nginx",
|
|
ociChartTag: "v1",
|
|
},
|
|
{
|
|
name: "no tag",
|
|
chartPath: "chart:5000/nginx",
|
|
ociChartURL: "oci://chart:5000/nginx",
|
|
ociChartTag: "",
|
|
},
|
|
{
|
|
name: "digest only",
|
|
chartPath: "ghcr.io/nginxinc/charts/nginx-ingress@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
|
ociChartURL: "oci://ghcr.io/nginxinc/charts/nginx-ingress@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
|
ociChartTag: "",
|
|
},
|
|
{
|
|
name: "version and digest",
|
|
chartPath: "ghcr.io/nginxinc/charts/nginx-ingress:2.0.0@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
|
ociChartURL: "oci://ghcr.io/nginxinc/charts/nginx-ingress@sha256:87ad282a8e7cc31913ce0543de2933ddb3f3eba80d6e5285f33b62ed720fc085",
|
|
ociChartTag: "2.0.0",
|
|
},
|
|
{
|
|
name: "port with digest",
|
|
chartPath: "registry:5000/chart@sha256:abc123",
|
|
ociChartURL: "oci://registry:5000/chart@sha256:abc123",
|
|
ociChartTag: "",
|
|
},
|
|
{
|
|
name: "port with version and digest",
|
|
chartPath: "registry:5000/chart:1.0.0@sha256:abc123",
|
|
ociChartURL: "oci://registry:5000/chart@sha256:abc123",
|
|
ociChartTag: "1.0.0",
|
|
},
|
|
}
|
|
for i := range tests {
|
|
tt := tests[i]
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
url, tag := resolveOciChart(tt.chartPath)
|
|
if tt.ociChartURL != url || tt.ociChartTag != tag {
|
|
actual := fmt.Sprintf("ociChartURL->%s ociChartTag->%s", url, tag)
|
|
expected := fmt.Sprintf("ociChartURL->%s ociChartTag->%s", tt.ociChartURL, tt.ociChartTag)
|
|
t.Errorf("resolveOciChart()\nactual = %v\nexpect = %v", actual, expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_ShowChart(t *testing.T) {
|
|
showChartRunner := mockRunner{output: []byte("name: my-chart\nversion: 3.2.0\n")}
|
|
helm := &execer{
|
|
helmBinary: "helm",
|
|
version: semver.MustParse("3.3.2"),
|
|
logger: NewLogger(os.Stdout, "info"),
|
|
kubeContext: "dev",
|
|
runner: &showChartRunner,
|
|
}
|
|
|
|
metadata, err := helm.ShowChart("my-chart")
|
|
if err != nil {
|
|
t.Errorf("helmexec.ShowChart() - unexpected error: %v", err)
|
|
}
|
|
if metadata.Name != "my-chart" {
|
|
t.Errorf("helmexec.ShowChart() - expected chart name was %s, received: %s", "my-chart", metadata.Name)
|
|
}
|
|
if metadata.Version != "3.2.0" {
|
|
t.Errorf("helmexec.ShowChart() - expected chart version was %s, received: %s", "3.2.0", metadata.Version)
|
|
}
|
|
}
|
|
|
|
func Test_SetDisableForceUpdate(t *testing.T) {
|
|
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
if helm.options.DisableForceUpdate {
|
|
t.Error("helmexec.options.DisableForceUpdate should not be enabled by default")
|
|
}
|
|
helm.SetDisableForceUpdate(true)
|
|
if !helm.options.DisableForceUpdate {
|
|
t.Errorf("helmexec.SetDisableForceUpdate() - actual = %t expect = true", helm.options.DisableForceUpdate)
|
|
}
|
|
}
|
|
|
|
func TestParseHelmVersion(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
version string
|
|
want *semver.Version
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "helm 2",
|
|
version: "Client: v2.16.1+ge13bc94\n",
|
|
want: semver.MustParse("v2.16.1+ge13bc94"),
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "helm 3",
|
|
version: "Client: v3.2.4+ge29ce2a\n",
|
|
want: semver.MustParse("v3.2.4+ge29ce2a"),
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "helm 3 with os arch and build info",
|
|
version: "Client v3.7.1+7.el8+g8f33223\n",
|
|
want: semver.MustParse("v3.7.1+7.el8"),
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "empty version",
|
|
version: "",
|
|
want: nil,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid version",
|
|
version: "oooooo",
|
|
want: nil,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "version without v prefix",
|
|
version: "3.2.4",
|
|
want: semver.MustParse("v3.2.4"),
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "version without v prefix with build info",
|
|
version: "3.2.4+ge29ce2a",
|
|
want: semver.MustParse("v3.2.4+ge29ce2a"),
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "version without v prefix with spaces",
|
|
version: " 3.2.4 ",
|
|
want: semver.MustParse("v3.2.4"),
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := parseHelmVersion(tt.version)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("parseHelmVersion() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("parseHelmVersion() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_helmSecretsRequiresSplitInstall(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
version string
|
|
want bool
|
|
}{
|
|
{name: "below threshold", version: "v4.6.9", want: false},
|
|
{name: "exactly at threshold", version: "v4.7.0", want: true},
|
|
{name: "above threshold single digit minor", version: "v4.8.0", want: true},
|
|
{name: "above threshold double digit minor (v4.10.0+)", version: "v4.10.0", want: true},
|
|
{name: "pre-release below threshold", version: "v4.7.0-beta.1", want: false},
|
|
{name: "invalid version string", version: "not-a-version", want: false},
|
|
{name: "empty string", version: "", want: false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := helmSecretsRequiresSplitInstall(tt.version)
|
|
assert.Equal(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
type funcRunner struct {
|
|
execute func(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error)
|
|
}
|
|
|
|
func (r *funcRunner) ExecuteStdIn(cmd string, args []string, env map[string]string, stdin io.Reader) ([]byte, error) {
|
|
return r.execute(cmd, args, env, false)
|
|
}
|
|
|
|
func (r *funcRunner) Execute(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error) {
|
|
return r.execute(cmd, args, env, enableLiveOutput)
|
|
}
|
|
|
|
func Test_UpdatePlugin_Helm4SecretsUsesUninstallReinstall(t *testing.T) {
|
|
var calledArgs [][]string
|
|
runner := &funcRunner{
|
|
execute: func(cmd string, args []string, env map[string]string, enableLiveOutput bool) ([]byte, error) {
|
|
calledArgs = append(calledArgs, append([]string(nil), args...))
|
|
return []byte{}, nil
|
|
},
|
|
}
|
|
|
|
var buffer bytes.Buffer
|
|
logger := NewLogger(&buffer, "debug")
|
|
helm := &execer{
|
|
helmBinary: "helm",
|
|
version: semver.MustParse("4.0.0"),
|
|
logger: logger,
|
|
kubeconfig: "config",
|
|
kubeContext: "dev",
|
|
runner: runner,
|
|
}
|
|
|
|
err := helm.UpdatePlugin("secrets", "https://github.com/jkroepke/helm-secrets", "v4.7.0")
|
|
require.NoError(t, err)
|
|
|
|
// Verify that "plugin update" was NOT called (the Helm 4 secrets path should skip it).
|
|
for _, args := range calledArgs {
|
|
for i, a := range args {
|
|
if a == "plugin" && i+1 < len(args) && args[i+1] == "update" {
|
|
t.Errorf("expected 'plugin update' to not be called for Helm 4 secrets, but it was: %v", args)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that uninstall was called for all three split plugins.
|
|
checkUninstall := func(name string) {
|
|
for _, args := range calledArgs {
|
|
for i, a := range args {
|
|
if a == "plugin" && i+2 < len(args) && args[i+1] == "uninstall" && args[i+2] == name {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
t.Errorf("expected 'plugin uninstall %s' to be called", name)
|
|
}
|
|
checkUninstall("secrets")
|
|
checkUninstall("secrets-getter")
|
|
checkUninstall("secrets-post-renderer")
|
|
|
|
// Verify that install was called for all three split plugin tarballs.
|
|
checkInstall := func(urlSubstring string) {
|
|
for _, args := range calledArgs {
|
|
for i, a := range args {
|
|
if a == "plugin" && i+2 < len(args) && args[i+1] == "install" && strings.Contains(args[i+2], urlSubstring) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
t.Errorf("expected 'plugin install' for %q to be called", urlSubstring)
|
|
}
|
|
checkInstall("secrets-4.7.0.tgz")
|
|
checkInstall("secrets-getter-4.7.0.tgz")
|
|
checkInstall("secrets-post-renderer-4.7.0.tgz")
|
|
}
|
|
|
|
func Test_dedupeWroteLines(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
}{
|
|
{
|
|
name: "empty",
|
|
in: "",
|
|
want: "",
|
|
},
|
|
{
|
|
name: "no wrote lines untouched",
|
|
in: "Found 25 resources in manifest for release system\n",
|
|
want: "Found 25 resources in manifest for release system\n",
|
|
},
|
|
{
|
|
name: "collapses duplicate wrote lines preserving first-seen order",
|
|
in: "wrote /tmp/a/service_account.yaml\n" +
|
|
"wrote /tmp/a/service_account.yaml\n" +
|
|
"wrote /tmp/a/secrets.yaml\n" +
|
|
"wrote /tmp/a/service_account.yaml\n" +
|
|
"wrote /tmp/a/secrets.yaml\n" +
|
|
"Found 25 resources in manifest for release system",
|
|
want: "wrote /tmp/a/service_account.yaml\n" +
|
|
"wrote /tmp/a/secrets.yaml\n" +
|
|
"Found 25 resources in manifest for release system",
|
|
},
|
|
{
|
|
name: "non-wrote duplicates kept",
|
|
in: "hello\nhello\n",
|
|
want: "hello\nhello\n",
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
got := string(dedupeWroteLines([]byte(c.in)))
|
|
if got != c.want {
|
|
t.Errorf("dedupeWroteLines() = %q, want %q", got, c.want)
|
|
}
|
|
})
|
|
}
|
|
}
|