From ec60ac815be394a15be19a66f28aed1a749664ce Mon Sep 17 00:00:00 2001 From: Yusuke Kuoka Date: Mon, 23 Jan 2023 18:27:00 +0900 Subject: [PATCH] feat: Add missingFileHandlerConfig.ignoreMissingGitBranch (#645) --- docs/index.md | 8 ++++++ pkg/state/create.go | 2 +- pkg/state/environment.go | 2 ++ pkg/state/state.go | 21 +++++++++++++--- pkg/state/storage.go | 34 ++++++++++++++++++++++--- pkg/state/storage_test.go | 53 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 111 insertions(+), 9 deletions(-) diff --git a/docs/index.md b/docs/index.md index c2d4178a..9c5aafa3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -236,6 +236,10 @@ releases: version: ~1.24.1 # the semver of the chart. range constraint is supported condition: vault.enabled # The values lookup key for filtering releases. Corresponds to the boolean value of `vault.enabled`, where `vault` is an arbitrary value missingFileHandler: Warn # 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. + 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 # Values files used for rendering the chart values: # Value files passed via --values @@ -400,6 +404,10 @@ environments: # Use "Warn", "Info", or "Debug" if you want helmfile to not fail when a values file is missing, while just leaving # a message about the missing file at the log-level. 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 # kubeContext to use for this environment kubeContext: kube-context diff --git a/pkg/state/create.go b/pkg/state/create.go index 7f466951..4e475b34 100644 --- a/pkg/state/create.go +++ b/pkg/state/create.go @@ -227,7 +227,7 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn if len(envSpec.Secrets) > 0 { var envSecretFiles []string for _, urlOrPath := range envSpec.Secrets { - resolved, skipped, err := st.storage().resolveFile(envSpec.MissingFileHandler, "environment values", urlOrPath) + resolved, skipped, err := st.storage().resolveFile(envSpec.MissingFileHandler, "environment values", urlOrPath, envSpec.MissingFileHandlerConfig.resolveFileOptions()...) if err != nil { return nil, err } diff --git a/pkg/state/environment.go b/pkg/state/environment.go index 13b1c137..de4e9db4 100644 --- a/pkg/state/environment.go +++ b/pkg/state/environment.go @@ -13,4 +13,6 @@ type EnvironmentSpec struct { // Use "Warn", "Info", or "Debug" if you want helmfile to not fail when a values file is missing, while just leaving // 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"` } diff --git a/pkg/state/state.go b/pkg/state/state.go index edee8ba8..edb6fc94 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -82,10 +82,19 @@ type ReleaseSetSpec struct { // non-existent path. The default behavior is to print a warning. Note the // differing default compared to other MissingFileHandlers. MissingFileHandler string `yaml:"missingFileHandler,omitempty"` + // MissingFileHandlerConfig is composed of various settings for the MissingFileHandler + MissingFileHandlerConfig MissingFileHandlerConfig `yaml:"missingFileHandlerConfig,omitempty"` LockFile string `yaml:"lockFilePath,omitempty"` } +type MissingFileHandlerConfig struct { + // IgnoreMissingGitBranch is set to true in order to let the missing file handler + // treat missing git branch errors like `pathspec 'develop' did not match any file(s) known to git` safe + // and ignored when the handler is set to Warn or Info. + IgnoreMissingGitBranch bool `yaml:"ignoreMissingGitBranch,omitempty"` +} + // helmStateAlias is helm state alias type helmStateAlias HelmState @@ -2721,13 +2730,19 @@ func (st *HelmState) removeFiles(files []string) { } } +func (c MissingFileHandlerConfig) resolveFileOptions() []resolveFileOption { + return []resolveFileOption{ + ignoreMissingGitBranch(c.IgnoreMissingGitBranch), + } +} + func (st *HelmState) generateTemporaryReleaseValuesFiles(release *ReleaseSpec, values []interface{}, missingFileHandler *string) ([]string, error) { generatedFiles := []string{} for _, value := range values { switch typedValue := value.(type) { case string: - paths, skip, err := st.storage().resolveFile(missingFileHandler, "values", typedValue) + paths, skip, err := st.storage().resolveFile(missingFileHandler, "values", typedValue, st.MissingFileHandlerConfig.resolveFileOptions()...) if err != nil { return generatedFiles, err } @@ -2828,7 +2843,7 @@ func (st *HelmState) generateSecretValuesFiles(helm helmexec.Interface, release switch value := v.(type) { case string: - paths, skip, err = st.storage().resolveFile(release.MissingFileHandler, "secrets", release.ValuesPathPrefix+value) + paths, skip, err = st.storage().resolveFile(release.MissingFileHandler, "secrets", release.ValuesPathPrefix+value, st.MissingFileHandlerConfig.resolveFileOptions()...) if err != nil { return nil, err } @@ -3282,7 +3297,7 @@ func (st *HelmState) LoadYAMLForEmbedding(release *ReleaseSpec, entries []interf case string: var values map[string]interface{} - paths, skip, err := st.storage().resolveFile(missingFileHandler, "values", pathPrefix+t) + paths, skip, err := st.storage().resolveFile(missingFileHandler, "values", pathPrefix+t, st.MissingFileHandlerConfig.resolveFileOptions()...) if err != nil { return nil, err } diff --git a/pkg/state/storage.go b/pkg/state/storage.go index a96b8817..68b7e217 100644 --- a/pkg/state/storage.go +++ b/pkg/state/storage.go @@ -5,6 +5,7 @@ import ( "net/url" "path/filepath" "sort" + "strings" "go.uber.org/zap" @@ -30,17 +31,42 @@ func NewStorage(forFile string, logger *zap.SugaredLogger, fs *filesystem.FileSy } } -func (st *Storage) resolveFile(missingFileHandler *string, tpe, path string) ([]string, bool, error) { +type resolveFileConfig struct { + IgnoreMissingGitBranch bool +} + +type resolveFileOption func(*resolveFileConfig) + +func ignoreMissingGitBranch(v bool) func(c *resolveFileConfig) { + return func(c *resolveFileConfig) { + c.IgnoreMissingGitBranch = v + } +} + +func (st *Storage) resolveFile(missingFileHandler *string, tpe, path string, opts ...resolveFileOption) ([]string, bool, error) { title := fmt.Sprintf("%s file", tpe) - var files []string - var err error + var ( + files []string + err error + conf resolveFileConfig + ) + + for _, o := range opts { + o(&conf) + } + if remote.IsRemote(path) { r := remote.NewRemote(st.logger, "", st.fs) fetchedFilePath, err := r.Fetch(path, "values") if err != nil { - return nil, false, err + // https://github.com/helmfile/helmfile/issues/392 + if conf.IgnoreMissingGitBranch && strings.Contains(err.Error(), "' did not match any file(s) known to git") { + st.logger.Debugf("Ignored missing git branch error: %v", err) + } else { + return nil, false, err + } } if st.fs.FileExistsAt(fetchedFilePath) { diff --git a/pkg/state/storage_test.go b/pkg/state/storage_test.go index b8651288..82d6da57 100644 --- a/pkg/state/storage_test.go +++ b/pkg/state/storage_test.go @@ -16,10 +16,12 @@ func TestStorage_resolveFile(t *testing.T) { missingFileHandler *string title string path string + opts []resolveFileOption } cacheDir := remote.CacheDir() infoHandler := MissingFileHandlerInfo + warnHandler := MissingFileHandlerWarn errorHandler := MissingFileHandlerError tests := []struct { @@ -49,6 +51,55 @@ func TestStorage_resolveFile(t *testing.T) { wantSkipped: false, wantErr: true, }, + { + name: "non existing branch in repo produce error", + args: args{ + path: "git::https://github.com/helmfile/helmfile.git@examples/values/non-existing-file.yaml?ref=inexistent-branch-for-test", + title: "values", + missingFileHandler: &infoHandler, + }, + wantSkipped: false, + wantErr: true, + }, + { + name: "non existing branch in repo produce info when ignoreMissingGitBranch=true", + args: args{ + path: "git::https://github.com/helmfile/helmfile.git@examples/values/non-existing-file.yaml?ref=inexistent-branch-for-test", + title: "values", + missingFileHandler: &infoHandler, + opts: []resolveFileOption{ + ignoreMissingGitBranch(true), + }, + }, + wantSkipped: true, + wantErr: false, + }, + { + name: "non existing branch in repo produce warn when ignoreMissingGitBranch=true", + args: args{ + path: "git::https://github.com/helmfile/helmfile.git@examples/values/non-existing-file.yaml?ref=inexistent-branch-for-test", + title: "values", + missingFileHandler: &warnHandler, + opts: []resolveFileOption{ + ignoreMissingGitBranch(true), + }, + }, + wantSkipped: true, + wantErr: false, + }, + { + name: "non existing branch in repo produce error with error handler even if ignoreMissingGitBranch=true", + args: args{ + path: "git::https://github.com/helmfile/helmfile.git@examples/values/non-existing-file.yaml?ref=inexistent-branch-for-test", + title: "values", + missingFileHandler: &errorHandler, + opts: []resolveFileOption{ + ignoreMissingGitBranch(true), + }, + }, + wantSkipped: false, + wantErr: true, + }, { name: "existing remote value fetched", args: args{ @@ -75,7 +126,7 @@ func TestStorage_resolveFile(t *testing.T) { t.Run(tt.name, func(t *testing.T) { st := NewStorage(cacheDir, helmexec.NewLogger(io.Discard, "debug"), filesystem.DefaultFileSystem()) - files, skipped, err := st.resolveFile(tt.args.missingFileHandler, tt.args.title, tt.args.path) + files, skipped, err := st.resolveFile(tt.args.missingFileHandler, tt.args.title, tt.args.path, tt.args.opts...) if (err != nil) != tt.wantErr { t.Errorf("resolveFile() error = %v, wantErr %v", err, tt.wantErr) return