diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 5380f225..b935c2a8 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -220,6 +220,97 @@ releases: } } +func TestUpdateStrategyParamValidation(t *testing.T) { + cases := []struct { + files map[string]string + updateStrategy string + isValid bool + }{ + {map[string]string{ + "/path/to/helmfile.yaml": `releases: +- name: zipkin + chart: stable/zipkin + updateStrategy: reinstall +`}, + "reinstall", + true}, + {map[string]string{ + "/path/to/helmfile.yaml": `releases: +- name: zipkin + chart: stable/zipkin + updateStrategy: reinstallIfForbidden +`}, + "reinstallIfForbidden", + true}, + {map[string]string{ + "/path/to/helmfile.yaml": `releases: +- name: zipkin + chart: stable/zipkin + updateStrategy: +`}, + "", + true}, + {map[string]string{ + "/path/to/helmfile.yaml": `releases: +- name: zipkin + chart: stable/zipkin + updateStrategy: foo +`}, + "foo", + false}, + {map[string]string{ + "/path/to/helmfile.yaml": `releases: +- name: zipkin + chart: stable/zipkin + updateStrategy: reinstal +`}, + "reinstal", + false}, + {map[string]string{ + "/path/to/helmfile.yaml": `releases: +- name: zipkin + chart: stable/zipkin + updateStrategy: reinstall1 +`}, + "reinstall1", + false}, + } + + for idx, c := range cases { + fs := testhelper.NewTestFs(c.files) + app := &App{ + OverrideHelmBinary: DefaultHelmBinary, + OverrideKubeContext: "default", + Logger: newAppTestLogger(), + Namespace: "", + Env: "default", + FileOrDir: "helmfile.yaml", + } + + expectNoCallsToHelm(app) + + app = injectFs(app, fs) + + err := app.ForEachState( + Noop, + false, + SetFilter(true), + ) + + if c.isValid && err != nil { + t.Errorf("[case: %d] Unexpected error for valid case: %v", idx, err) + } else if !c.isValid { + var invalidUpdateStrategy state.InvalidUpdateStrategyError + invalidUpdateStrategy.UpdateStrategy = c.updateStrategy + if err == nil { + t.Errorf("[case: %d] Expected error for invalid case", idx) + } else if !strings.Contains(err.Error(), invalidUpdateStrategy.Error()) { + t.Errorf("[case: %d] Unexpected error returned for invalid case\ngot: %v\nexpected underlying error: %s", idx, err, invalidUpdateStrategy.Error()) + } + } + } +} + func TestVisitDesiredStatesWithReleasesFiltered_Issue1008_MissingNonDefaultEnvInBase(t *testing.T) { files := map[string]string{ "/path/to/base.yaml": ` diff --git a/pkg/app/desired_state_file_loader.go b/pkg/app/desired_state_file_loader.go index 90986023..3e149ab1 100644 --- a/pkg/app/desired_state_file_loader.go +++ b/pkg/app/desired_state_file_loader.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "path/filepath" + "slices" "dario.cat/mergo" "github.com/helmfile/vals" @@ -285,6 +286,18 @@ func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, ba } } + // Validate updateStrategy value if set in the releases + for i := range finalState.Releases { + if finalState.Releases[i].UpdateStrategy != "" { + if !slices.Contains(state.ValidUpdateStrategyValues, finalState.Releases[i].UpdateStrategy) { + return nil, &state.StateLoadError{ + Msg: fmt.Sprintf("failed to read %s", finalState.FilePath), + Cause: &state.InvalidUpdateStrategyError{UpdateStrategy: finalState.Releases[i].UpdateStrategy}, + } + } + } + } + finalState.OrginReleases = finalState.Releases return finalState, nil } diff --git a/pkg/state/create.go b/pkg/state/create.go index b175faf9..7874b9dc 100644 --- a/pkg/state/create.go +++ b/pkg/state/create.go @@ -26,6 +26,8 @@ const ( DefaultHCLFileExtension = ".hcl" ) +var ValidUpdateStrategyValues = []string{UpdateStrategyReinstall, UpdateStrategyReinstallIfForbidden} + type StateLoadError struct { Msg string Cause error @@ -43,6 +45,14 @@ func (e *UndefinedEnvError) Error() string { return fmt.Sprintf("environment \"%s\" is not defined", e.Env) } +type InvalidUpdateStrategyError struct { + UpdateStrategy string +} + +func (e *InvalidUpdateStrategyError) Error() string { + return fmt.Sprintf("updateStrategy %q is invalid, valid values are: %s or not set", e.UpdateStrategy, strings.Join(ValidUpdateStrategyValues, ", ")) +} + type StateCreator struct { logger *zap.SugaredLogger diff --git a/pkg/state/state.go b/pkg/state/state.go index 9b121851..d451105e 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -43,6 +43,10 @@ const ( // This is used by an interim solution to make the urfave/cli command report to the helmfile internal about that the // --timeout flag is missingl EmptyTimeout = -1 + + // Valid enum for updateStrategy values + UpdateStrategyReinstall = "reinstall" + UpdateStrategyReinstallIfForbidden = "reinstallIfForbidden" ) // ReleaseSetSpec is release set spec