diff --git a/docs/index.md b/docs/index.md index 00aaf902..ec7ee6ff 100644 --- a/docs/index.md +++ b/docs/index.md @@ -412,6 +412,10 @@ helmfiles: # If set to "Error", return an error when a subhelmfile points to a # non-existent path. The default behavior is to print a warning and continue. missingFileHandler: Error +missingFileHandlerConfig: + # Ignores missing git branch error so that the Debug/Info/Warn handler can treat a missing branch as non-error. + # See https://github.com/helmfile/helmfile/issues/392 + ignoreMissingGitBranch: true # # Advanced Configuration: Environments diff --git a/pkg/state/create.go b/pkg/state/create.go index b175faf9..68c54b1d 100644 --- a/pkg/state/create.go +++ b/pkg/state/create.go @@ -224,6 +224,31 @@ func (c *StateCreator) loadBases(envValues, overrodeEnv *environment.Environment return layers[0], nil } +// getEnvMissingFileHandlerConfig returns the first non-nil MissingFileHandlerConfig from the environment spec, state, or default. +func (st *HelmState) getEnvMissingFileHandlerConfig(es EnvironmentSpec) *MissingFileHandlerConfig { + switch { + case es.MissingFileHandlerConfig != nil: + return es.MissingFileHandlerConfig + case st.MissingFileHandlerConfig != nil: + return st.MissingFileHandlerConfig + default: + return nil + } +} + +// getEnvMissingFileHandler returns the first non-nil MissingFileHandler from the environment spec, state, or default. +func (st *HelmState) getEnvMissingFileHandler(es EnvironmentSpec) *string { + defaultMissingFileHandler := "Error" + switch { + case es.MissingFileHandler != nil: + return es.MissingFileHandler + case st.MissingFileHandler != nil: + return st.MissingFileHandler + default: + return &defaultMissingFileHandler + } +} + // nolint: unparam func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEnv bool, ctxEnv, overrode *environment.Environment) (*environment.Environment, error) { secretVals := map[string]any{} @@ -240,7 +265,7 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn var envSecretFiles []string if len(envSpec.Secrets) > 0 { for _, urlOrPath := range envSpec.Secrets { - resolved, skipped, err := st.storage().resolveFile(envSpec.MissingFileHandler, "environment values", urlOrPath, envSpec.MissingFileHandlerConfig.resolveFileOptions()...) + resolved, skipped, err := st.storage().resolveFile(st.getEnvMissingFileHandler(envSpec), "environment values", urlOrPath, st.getEnvMissingFileHandlerConfig(envSpec).resolveFileOptions()...) if err != nil { return nil, err } diff --git a/pkg/state/environment.go b/pkg/state/environment.go index 4afa9141..c5c78876 100644 --- a/pkg/state/environment.go +++ b/pkg/state/environment.go @@ -14,5 +14,5 @@ type EnvironmentSpec struct { // a message about the missing file at the log-level. MissingFileHandler *string `yaml:"missingFileHandler,omitempty"` // MissingFileHandlerConfig is composed of various settings for the MissingFileHandler - MissingFileHandlerConfig MissingFileHandlerConfig `yaml:"missingFileHandlerConfig,omitempty"` + MissingFileHandlerConfig *MissingFileHandlerConfig `yaml:"missingFileHandlerConfig,omitempty"` } diff --git a/pkg/state/helmx.go b/pkg/state/helmx.go index 1a46b1d5..eca4b2cb 100644 --- a/pkg/state/helmx.go +++ b/pkg/state/helmx.go @@ -350,7 +350,7 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp jsonPatches := release.JSONPatches if len(jsonPatches) > 0 { - generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, jsonPatches, release.MissingFileHandler) + generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, jsonPatches) if err != nil { return nil, clean, err } @@ -364,7 +364,7 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp strategicMergePatches := release.StrategicMergePatches if len(strategicMergePatches) > 0 { - generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, strategicMergePatches, release.MissingFileHandler) + generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, strategicMergePatches) if err != nil { return nil, clean, err } @@ -378,7 +378,7 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp transformers := release.Transformers if len(transformers) > 0 { - generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, transformers, release.MissingFileHandler) + generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, transformers) if err != nil { return nil, clean, err } diff --git a/pkg/state/state.go b/pkg/state/state.go index b167fa88..428cf414 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -84,9 +84,9 @@ type ReleaseSetSpec struct { // If set to "Error", return an error when a subhelmfile points to a // non-existent path. The default behavior is to print a warning. Note the // differing default compared to other MissingFileHandlers. - MissingFileHandler string `yaml:"missingFileHandler,omitempty"` + MissingFileHandler *string `yaml:"missingFileHandler,omitempty"` // MissingFileHandlerConfig is composed of various settings for the MissingFileHandler - MissingFileHandlerConfig MissingFileHandlerConfig `yaml:"missingFileHandlerConfig,omitempty"` + MissingFileHandlerConfig *MissingFileHandlerConfig `yaml:"missingFileHandlerConfig,omitempty"` LockFile string `yaml:"lockFilePath,omitempty"` } @@ -307,6 +307,10 @@ type ReleaseSpec struct { // MissingFileHandler is set to either "Error" or "Warn". "Error" instructs helmfile to fail when unable to find a values or secrets file. When "Warn", it prints the file and continues. // The default value for MissingFileHandler is "Error". MissingFileHandler *string `yaml:"missingFileHandler,omitempty"` + + // MissingFileHandlerConfig is composed of various settings for the MissingFileHandler + MissingFileHandlerConfig *MissingFileHandlerConfig `yaml:"missingFileHandlerConfig,omitempty"` + // Needs is the [KUBECONTEXT/][NS/]NAME representations of releases that this release depends on. Needs []string `yaml:"needs,omitempty"` @@ -3194,7 +3198,7 @@ func (st *HelmState) ExpandedHelmfiles() ([]SubHelmfileSpec, error) { } if len(matches) == 0 { err := fmt.Errorf("no matches for path: %s", hf.Path) - if st.MissingFileHandler == "Error" { + if *st.MissingFileHandler == "Error" { return nil, err } st.logger.Warnf("no matches for path: %s", hf.Path) @@ -3241,19 +3245,54 @@ func (st *HelmState) removeFiles(files []string) { } } -func (c MissingFileHandlerConfig) resolveFileOptions() []resolveFileOption { +func (c *MissingFileHandlerConfig) resolveFileOptions() []resolveFileOption { + if c == nil { + return []resolveFileOption{ + ignoreMissingGitBranch(false), + } + } return []resolveFileOption{ ignoreMissingGitBranch(c.IgnoreMissingGitBranch), } } -func (st *HelmState) generateTemporaryReleaseValuesFiles(release *ReleaseSpec, values []any, missingFileHandler *string) ([]string, error) { +// getReleaseMissingFileHandlerConfig returns the first non-nil MissingFileHandlerConfig in the following order: +// - release.MissingFileHandlerConfig +// - st.MissingFileHandlerConfig +func (st *HelmState) getReleaseMissingFileHandlerConfig(release *ReleaseSpec) *MissingFileHandlerConfig { + switch { + case release.MissingFileHandlerConfig != nil: + return release.MissingFileHandlerConfig + case st.MissingFileHandlerConfig != nil: + return st.MissingFileHandlerConfig + default: + return nil + } +} + +// getReleaseMissingFileHandler returns the first non-nil MissingFileHandler in the following order: +// - release.MissingFileHandler +// - st.MissingFileHandler +// - "Error" +func (st *HelmState) getReleaseMissingFileHandler(release *ReleaseSpec) *string { + defaultMissingFileHandler := "Error" + switch { + case release.MissingFileHandler != nil: + return release.MissingFileHandler + case st.MissingFileHandler != nil: + return st.MissingFileHandler + default: + return &defaultMissingFileHandler + } +} + +func (st *HelmState) generateTemporaryReleaseValuesFiles(release *ReleaseSpec, values []any) ([]string, error) { generatedFiles := []string{} for _, value := range values { switch typedValue := value.(type) { case string: - paths, skip, err := st.storage().resolveFile(missingFileHandler, "values", typedValue, st.MissingFileHandlerConfig.resolveFileOptions()...) + paths, skip, err := st.storage().resolveFile(st.getReleaseMissingFileHandler(release), "values", typedValue, st.getReleaseMissingFileHandlerConfig(release).resolveFileOptions()...) if err != nil { return generatedFiles, err } @@ -3334,7 +3373,7 @@ func (st *HelmState) generateVanillaValuesFiles(release *ReleaseSpec) ([]string, return nil, fmt.Errorf("Failed to render values in %s for release %s: type %T isn't supported", st.FilePath, release.Name, valuesMapSecretsRendered["values"]) } - generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, valuesSecretsRendered, release.MissingFileHandler) + generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, valuesSecretsRendered) if err != nil { return nil, err } @@ -3400,7 +3439,7 @@ func (st *HelmState) generateSecretValuesFiles(helm helmexec.Interface, release generatedDecryptedFiles = append(generatedDecryptedFiles, valfile) } - generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, generatedDecryptedFiles, release.MissingFileHandler) + generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, generatedDecryptedFiles) if err != nil { return nil, err } diff --git a/pkg/state/temp_test.go b/pkg/state/temp_test.go index 90ef78a9..ca3f493c 100644 --- a/pkg/state/temp_test.go +++ b/pkg/state/temp_test.go @@ -38,39 +38,39 @@ func TestGenerateID(t *testing.T) { run(testcase{ subject: "baseline", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, - want: "foo-values-57ff559cd5", + want: "foo-values-7d454b9558", }) run(testcase{ subject: "different bytes content", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, data: []byte(`{"k":"v"}`), - want: "foo-values-75cd785b8b", + want: "foo-values-59c86d55bf", }) run(testcase{ subject: "different map content", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, data: map[string]any{"k": "v"}, - want: "foo-values-5b44fdc768", + want: "foo-values-6f87c5cd79", }) run(testcase{ subject: "different chart", release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"}, - want: "foo-values-6ddb5db94d", + want: "foo-values-5dfd748475", }) run(testcase{ subject: "different name", release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"}, - want: "bar-values-79bbb8df74", + want: "bar-values-858b9c55cc", }) run(testcase{ subject: "specific ns", release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"}, - want: "myns-foo-values-c9457ccfb", + want: "myns-foo-values-58dc9c6667", }) for id, n := range ids {