From f708d06200b5c0062e8c4491ae2878683f65a915 Mon Sep 17 00:00:00 2001 From: Nick Neisen <5108029+nwneisen@users.noreply.github.com> Date: Mon, 8 Sep 2025 17:15:46 -0600 Subject: [PATCH] Fix panic when helm isn't installed (#2169) Return error instead of panic Signed-off-by: Nick Neisen --- pkg/app/app.go | 15 +- pkg/app/app_test.go | 9 +- pkg/app/desired_state_file_loader.go | 2 +- pkg/app/init.go | 5 +- pkg/helmexec/exec.go | 7 +- pkg/helmexec/exec_test.go | 212 ++++++++++++++++++++------- pkg/helmexec/runner.go | 2 +- pkg/state/create.go | 9 +- 8 files changed, 189 insertions(+), 72 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index 19024459..badf2ecd 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -784,7 +784,7 @@ func createHelmKey(bin, kubectx string) helmKey { // // This is currently used for running all the helm commands for reconciling releases. But this may change in the future // once we enable each release to have its own helm binary/version. -func (a *App) getHelm(st *state.HelmState) helmexec.Interface { +func (a *App) getHelm(st *state.HelmState) (helmexec.Interface, error) { a.helmsMutex.Lock() defer a.helmsMutex.Unlock() @@ -799,14 +799,18 @@ func (a *App) getHelm(st *state.HelmState) helmexec.Interface { key := createHelmKey(bin, kubectx) if _, ok := a.helms[key]; !ok { - a.helms[key] = helmexec.New(bin, helmexec.HelmExecOptions{EnableLiveOutput: a.EnableLiveOutput, DisableForceUpdate: a.DisableForceUpdate}, a.Logger, kubeconfig, kubectx, &helmexec.ShellRunner{ + exec, err := helmexec.New(bin, helmexec.HelmExecOptions{EnableLiveOutput: a.EnableLiveOutput, DisableForceUpdate: a.DisableForceUpdate}, a.Logger, kubeconfig, kubectx, &helmexec.ShellRunner{ Logger: a.Logger, Ctx: a.ctx, StripArgsValuesOnExitError: a.StripArgsValuesOnExitError, }) + if err != nil { + return nil, err + } + a.helms[key] = exec } - return a.helms[key] + return a.helms[key], nil } func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*state.HelmState) (bool, []error)) error { @@ -958,7 +962,10 @@ var ( func (a *App) ForEachState(do func(*Run) (bool, []error), includeTransitiveNeeds bool, o ...LoadOption) error { ctx := NewContext() err := a.visitStatesWithSelectorsAndRemoteSupport(a.FileOrDir, func(st *state.HelmState) (bool, []error) { - helm := a.getHelm(st) + helm, err := a.getHelm(st) + if err != nil { + return false, []error{err} + } run, err := NewRun(st, helm, ctx) if err != nil { diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index fde002c3..cf38aa2e 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2492,9 +2492,12 @@ func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string return []byte{}, nil } -func MockExecer(logger *zap.SugaredLogger, kubeContext string) helmexec.Interface { - execer := helmexec.New("helm", helmexec.HelmExecOptions{}, logger, "", kubeContext, &mockRunner{}) - return execer +func MockExecer(logger *zap.SugaredLogger, kubeContext string) (helmexec.Interface, error) { + execer, err := helmexec.New("helm", helmexec.HelmExecOptions{}, logger, "", kubeContext, &mockRunner{}) + if err != nil { + return nil, err + } + return execer, nil } // mocking helmexec.Interface diff --git a/pkg/app/desired_state_file_loader.go b/pkg/app/desired_state_file_loader.go index 90986023..be176bf9 100644 --- a/pkg/app/desired_state_file_loader.go +++ b/pkg/app/desired_state_file_loader.go @@ -34,7 +34,7 @@ type desiredStateLoader struct { chart string fs *filesystem.FileSystem - getHelm func(*state.HelmState) helmexec.Interface + getHelm func(*state.HelmState) (helmexec.Interface, error) remote *remote.Remote logger *zap.SugaredLogger diff --git a/pkg/app/init.go b/pkg/app/init.go index e90b2a38..57b0027e 100644 --- a/pkg/app/init.go +++ b/pkg/app/init.go @@ -163,7 +163,10 @@ func (h *HelmfileInit) WhetherContinue(ask string) error { func (h *HelmfileInit) CheckHelmPlugins() error { settings := cli.New() - helm := helmexec.New(h.helmBinary, helmexec.HelmExecOptions{}, h.logger, "", "", h.runner) + helm, err := helmexec.New(h.helmBinary, helmexec.HelmExecOptions{}, h.logger, "", "", h.runner) + if err != nil { + return err + } for _, p := range helmPlugins { pluginVersion, err := helmexec.GetPluginVersion(p.name, settings.PluginsDirectory) if err != nil { diff --git a/pkg/helmexec/exec.go b/pkg/helmexec/exec.go index e2df4423..fd185b71 100644 --- a/pkg/helmexec/exec.go +++ b/pkg/helmexec/exec.go @@ -122,11 +122,10 @@ func redactedURL(chart string) string { } // New for running helm commands -func New(helmBinary string, options HelmExecOptions, logger *zap.SugaredLogger, kubeconfig string, kubeContext string, runner Runner) *execer { - // TODO: proper error handling +func New(helmBinary string, options HelmExecOptions, logger *zap.SugaredLogger, kubeconfig string, kubeContext string, runner Runner) (*execer, error) { version, err := GetHelmVersion(helmBinary, runner) if err != nil { - panic(err) + return nil, err } if version.Prerelease() != "" { @@ -143,7 +142,7 @@ func New(helmBinary string, options HelmExecOptions, logger *zap.SugaredLogger, kubeContext: kubeContext, runner: runner, decryptedSecrets: make(map[string]*decryptedSecret), - } + }, nil } func (helm *execer) SetExtraArgs(args ...string) { diff --git a/pkg/helmexec/exec_test.go b/pkg/helmexec/exec_test.go index bbf88808..3c4ebfff 100644 --- a/pkg/helmexec/exec_test.go +++ b/pkg/helmexec/exec_test.go @@ -36,16 +36,22 @@ func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string return mock.output, mock.err } -func MockExecer(logger *zap.SugaredLogger, kubeconfig, kubeContext string) *execer { - execer := New("helm", HelmExecOptions{}, logger, kubeconfig, kubeContext, &mockRunner{}) - return execer +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 := MockExecer(NewLogger(buffer, "debug"), "config", "dev") + helm, err := MockExecer(NewLogger(buffer, "debug"), "config", "dev") + if err != nil { + t.Error(err) + } if helm.kubeContext != "dev" { t.Error("helmexec.New() - kubeContext") } @@ -58,7 +64,10 @@ func TestNewHelmExec(t *testing.T) { } func Test_SetExtraArgs(t *testing.T) { - helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev") + 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") @@ -74,7 +83,10 @@ func Test_SetExtraArgs(t *testing.T) { } func Test_SetHelmBinary(t *testing.T) { - helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev") + 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") } @@ -85,7 +97,10 @@ func Test_SetHelmBinary(t *testing.T) { } func Test_SetEnableLiveOutput(t *testing.T) { - helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev") + 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") } @@ -96,7 +111,10 @@ func Test_SetEnableLiveOutput(t *testing.T) { } func Test_SetDisableForceUpdate(t *testing.T) { - helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev") + helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev") + if err != nil { + t.Error(err) + } if helm.options.DisableForceUpdate { t.Error("helmexec.options.ForceUpdate should not be enabled by default") } @@ -155,11 +173,14 @@ exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.e func Test_AddRepo(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") + 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) + 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/ --cert-file cert.pem --key-file key.pem ` @@ -292,8 +313,11 @@ exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.e func Test_UpdateRepo(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.UpdateRepo() + 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 ` @@ -346,8 +370,11 @@ exec: helm --kubeconfig config --kube-context dev registry login repo.example.co func Test_SyncRelease(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.SyncRelease(HelmContext{}, "release", "chart", "default", "--timeout 10", "--wait", "--wait-for-jobs") + 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 ` @@ -386,8 +413,11 @@ exec: helm --kubeconfig config --kube-context dev upgrade --install release http func Test_UpdateDeps(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.UpdateDeps("./chart/foo") + 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 ` @@ -416,8 +446,11 @@ func Test_BuildDeps(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") helm3Runner := mockRunner{output: []byte("v3.2.4+ge29ce2a")} - helm := New("helm", HelmExecOptions{}, logger, "config", "dev", &helm3Runner) - err := helm.BuildDeps("foo", "./chart/foo", []string{"--skip-refresh"}...) + 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 @@ -458,7 +491,10 @@ v3.2.4+ge29ce2a buffer.Reset() helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94")} - helm = New("helm", HelmExecOptions{}, logger, "config", "dev", &helm2Runner) + 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 @@ -484,14 +520,17 @@ func Test_DecryptSecret(t *testing.T) { }() var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") + 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") + _, err = helm.DecryptSecret(HelmContext{}, "secretName") if err != nil { t.Errorf("Error: %v", err) } @@ -533,7 +572,10 @@ func Test_DecryptSecretWithGotmpl(t *testing.T) { }() var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") + 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) { @@ -541,7 +583,7 @@ func Test_DecryptSecretWithGotmpl(t *testing.T) { } secretName := "secretName.yaml.gotmpl" - _, err := helm.DecryptSecret(HelmContext{}, secretName) + _, err = helm.DecryptSecret(HelmContext{}, secretName) if err != nil { t.Errorf("Error: %v", err) } @@ -566,8 +608,11 @@ Decrypted %s/secretName.yaml.gotmpl into %s func Test_DiffRelease(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.DiffRelease(HelmContext{}, "release", "chart", "default", false, "--timeout 10", "--wait", "--wait-for-jobs") + 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 @@ -609,8 +654,11 @@ exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unrelease func Test_DeleteRelease(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.DeleteRelease(HelmContext{}, "release") + 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 ` @@ -624,8 +672,11 @@ exec: helm --kubeconfig config --kube-context dev delete release func Test_DeleteRelease_Flags(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.DeleteRelease(HelmContext{}, "release", "--purge") + 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 ` @@ -640,8 +691,11 @@ exec: helm --kubeconfig config --kube-context dev delete release --purge func Test_TestRelease(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.TestRelease(HelmContext{}, "release") + 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 ` @@ -655,8 +709,11 @@ exec: helm --kubeconfig config --kube-context dev test release func Test_TestRelease_Flags(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.TestRelease(HelmContext{}, "release", "--cleanup", "--timeout", "60") + 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 ` @@ -671,8 +728,11 @@ exec: helm --kubeconfig config --kube-context dev test release --cleanup --timeo func Test_ReleaseStatus(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.ReleaseStatus(HelmContext{}, "myRelease") + 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 ` @@ -687,9 +747,12 @@ exec: helm --kubeconfig config --kube-context dev status myRelease func Test_exec(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "", "") + helm, err := MockExecer(logger, "", "") + if err != nil { + t.Errorf("unexpected error: %v", err) + } env := map[string]string{} - _, err := helm.exec([]string{"version"}, env, nil) + _, err = helm.exec([]string{"version"}, env, nil) expected := `exec: helm version ` if err != nil { @@ -699,14 +762,20 @@ func Test_exec(t *testing.T) { t.Errorf("helmexec.exec()\nactual = %v\nexpect = %v", buffer.String(), expected) } - helm = MockExecer(logger, "config", "dev") + 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 = MockExecer(logger, "config", "dev") + 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 ` @@ -741,7 +810,10 @@ func Test_exec(t *testing.T) { } buffer.Reset() - helm = MockExecer(logger, "", "") + 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 @@ -757,8 +829,11 @@ func Test_exec(t *testing.T) { func Test_Lint(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.Lint("release", "path/to/chart", "--values", "file.yml") + 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 ` @@ -773,8 +848,11 @@ exec: helm --kubeconfig config --kube-context dev lint path/to/chart --values fi func Test_Fetch(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.Fetch("chart", "--version", "1.2.3", "--untar", "--untardir", "/tmp/dir") + 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 ` @@ -848,8 +926,11 @@ exec: helm --kubeconfig config --kube-context dev pull oci://repo/helm-charts -- tt := tests[i] t.Run(tt.name, func(t *testing.T) { buffer.Reset() - helm := New(tt.helmBin, HelmExecOptions{}, logger, "config", "dev", &mockRunner{output: []byte(tt.helmVersion)}) - err := helm.ChartPull(tt.chartName, tt.chartPath, tt.chartFlags...) + 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) } @@ -922,8 +1003,11 @@ func Test_LogLevels(t *testing.T) { for logLevel, expected := range logLevelTests { buffer.Reset() logger := NewLogger(&buffer, logLevel) - helm := MockExecer(logger, "", "") - err := helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password", "", false, false) + 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) } @@ -951,8 +1035,11 @@ func Test_mergeEnv(t *testing.T) { func Test_Template(t *testing.T) { var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") - helm := MockExecer(logger, "config", "dev") - err := helm.TemplateRelease("release", "path/to/chart", "--values", "file.yml") + 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 ` @@ -978,13 +1065,19 @@ exec: helm --kubeconfig config --kube-context dev template release https://examp func Test_IsHelm3(t *testing.T) { helm2Runner := mockRunner{output: []byte("Client: v2.16.0+ge13bc94\n")} - helm := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner) + helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner) + if err != nil { + t.Errorf("unexpected error: %v", err) + } if helm.IsHelm3() { t.Error("helmexec.IsHelm3() - Detected Helm 3 with Helm 2 version") } helm3Runner := mockRunner{output: []byte("v3.0.0+ge29ce2a\n")} - helm = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner) + 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") } @@ -1012,14 +1105,20 @@ func Test_GetPluginVersion(t *testing.T) { func Test_GetVersion(t *testing.T) { helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")} - helm := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner) + 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 = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner) + 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) @@ -1028,7 +1127,10 @@ func Test_GetVersion(t *testing.T) { func Test_IsVersionAtLeast(t *testing.T) { helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")} - helm := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner) + 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") } diff --git a/pkg/helmexec/runner.go b/pkg/helmexec/runner.go index 1456caf9..8c0b374b 100644 --- a/pkg/helmexec/runner.go +++ b/pkg/helmexec/runner.go @@ -118,7 +118,7 @@ func Output(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError bool, l exitStatus := waitStatus.ExitStatus() err = newExitError(c.Path, c.Args, exitStatus, ee, stderr.String(), combined.String(), stripArgsValuesOnExitError) default: - panic(fmt.Sprintf("unexpected error: %v", err)) + err = fmt.Errorf("unexpected error: %v", err) } } diff --git a/pkg/state/create.go b/pkg/state/create.go index 68c54b1d..97e91823 100644 --- a/pkg/state/create.go +++ b/pkg/state/create.go @@ -54,7 +54,7 @@ type StateCreator struct { LoadFile func(inheritedEnv, overrodeEnv *environment.Environment, baseDir, file string, evaluateBases bool) (*HelmState, error) - getHelm func(*HelmState) helmexec.Interface + getHelm func(*HelmState) (helmexec.Interface, error) overrideHelmBinary string @@ -67,7 +67,7 @@ type StateCreator struct { lockFile string } -func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntime vals.Evaluator, getHelm func(*HelmState) helmexec.Interface, overrideHelmBinary string, overrideKustomizeBinary string, remote *remote.Remote, enableLiveOutput bool, lockFile string) *StateCreator { +func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntime vals.Evaluator, getHelm func(*HelmState) (helmexec.Interface, error), overrideHelmBinary string, overrideKustomizeBinary string, remote *remote.Remote, enableLiveOutput bool, lockFile string) *StateCreator { return &StateCreator{ logger: logger, @@ -342,7 +342,10 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles []string, envVals map[string]any, keepFileExtensions []string) ([]string, error) { var errs []error var decryptedFilesKeeper []string - helm := c.getHelm(st) + helm, err := c.getHelm(st) + if err != nil { + return nil, err + } inputs := envSecretFiles inputsSize := len(inputs)