feat: `helmfile --log-level=debug apply --retain-values-files` (#1127)

`--retain-values-files` prevents temporary values files that were passed to Helm commands run by Helmfile for debugging purpose.

With that, you can manually rerun helm commands that were logged when `--log-level=debug` is enabled.

Resolves ##1117
This commit is contained in:
KUOKA Yusuke 2020-02-28 19:39:01 +09:00 committed by GitHub
parent 0186254e79
commit af44965949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 22 deletions

View File

@ -338,6 +338,10 @@ func main() {
Value: "", Value: "",
Usage: "pass args to helm exec", Usage: "pass args to helm exec",
}, },
cli.BoolFlag{
Name: "retain-values-files",
Usage: "Stop cleaning up values files passed to Helm. Together with --log-level=debug, you can manually rerun helm commands as Helmfile did for debugging purpose",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "suppress-secrets", Name: "suppress-secrets",
Usage: "suppress secrets in the diff output. highly recommended to specify on CI/CD use-cases", Usage: "suppress secrets in the diff output. highly recommended to specify on CI/CD use-cases",
@ -542,6 +546,10 @@ func (c configImpl) DetailedExitcode() bool {
return c.c.Bool("detailed-exitcode") return c.c.Bool("detailed-exitcode")
} }
func (c configImpl) RetainValuesFiles() bool {
return c.c.Bool("retain-values-files")
}
func (c configImpl) SuppressSecrets() bool { func (c configImpl) SuppressSecrets() bool {
return c.c.Bool("suppress-secrets") return c.c.Bool("suppress-secrets")
} }

View File

@ -155,7 +155,7 @@ func (a *App) Apply(c ApplyConfigProvider) error {
mut.Unlock() mut.Unlock()
return matched, errs return matched, errs
}) }, c.RetainValuesFiles())
if err != nil { if err != nil {
return err return err
@ -343,7 +343,7 @@ func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*sta
sig := <-sigs sig := <-sigs
errs := []error{fmt.Errorf("Received [%s] to shutdown ", sig)} errs := []error{fmt.Errorf("Received [%s] to shutdown ", sig)}
_ = context{a, st}.clean(errs) _ = context{app: a, st: st, retainValues: defOpts.RetainValuesFiles}.clean(errs)
// See http://tldp.org/LDP/abs/html/exitcodes.html // See http://tldp.org/LDP/abs/html/exitcodes.html
switch sig { switch sig {
case syscall.SIGINT: case syscall.SIGINT:
@ -353,7 +353,7 @@ func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*sta
} }
}() }()
ctx := context{a, st} ctx := context{app: a, st: st, retainValues: defOpts.RetainValuesFiles}
helm := a.helmExecer helm := a.helmExecer
@ -409,7 +409,7 @@ func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*sta
processed, errs := converge(templated, helm) processed, errs := converge(templated, helm)
noMatchInHelmfiles = noMatchInHelmfiles && !processed noMatchInHelmfiles = noMatchInHelmfiles && !processed
return context{a, templated}.clean(errs) return context{app: a, st: templated, retainValues: defOpts.RetainValuesFiles}.clean(errs)
}) })
if err != nil { if err != nil {
@ -434,12 +434,12 @@ func (a *App) ForEachStateFiltered(do func(*Run) []error) error {
return err return err
} }
func (a *App) ForEachState(do func(*Run) (bool, []error)) error { func (a *App) ForEachState(do func(*Run) (bool, []error), retainValues ...bool) error {
ctx := NewContext() ctx := NewContext()
err := a.visitStatesWithSelectorsAndRemoteSupport(a.FileOrDir, func(st *state.HelmState, helm helmexec.Interface) (bool, []error) { err := a.visitStatesWithSelectorsAndRemoteSupport(a.FileOrDir, func(st *state.HelmState, helm helmexec.Interface) (bool, []error) {
run := NewRun(st, helm, ctx) run := NewRun(st, helm, ctx)
return do(run) return do(run)
}) }, retainValues...)
return err return err
} }
@ -511,11 +511,15 @@ type Opts struct {
DAGEnabled bool DAGEnabled bool
} }
func (a *App) visitStatesWithSelectorsAndRemoteSupport(fileOrDir string, converge func(*state.HelmState, helmexec.Interface) (bool, []error)) error { func (a *App) visitStatesWithSelectorsAndRemoteSupport(fileOrDir string, converge func(*state.HelmState, helmexec.Interface) (bool, []error), retainValues ...bool) error {
opts := LoadOpts{ opts := LoadOpts{
Selectors: a.Selectors, Selectors: a.Selectors,
} }
if len(retainValues) > 0 {
opts.RetainValuesFiles = retainValues[0]
}
envvals := []interface{}{} envvals := []interface{}{}
if a.ValuesFiles != nil { if a.ValuesFiles != nil {
@ -1222,10 +1226,12 @@ func (c context) clean(errs []error) error {
errs = []error{} errs = []error{}
} }
if !c.retainValues {
cleanErrs := c.st.Clean() cleanErrs := c.st.Clean()
if cleanErrs != nil { if cleanErrs != nil {
errs = append(errs, cleanErrs...) errs = append(errs, cleanErrs...)
} }
}
return c.wrapErrs(errs...) return c.wrapErrs(errs...)
} }
@ -1233,6 +1239,8 @@ func (c context) clean(errs []error) error {
type context struct { type context struct {
app *App app *App
st *state.HelmState st *state.HelmState
retainValues bool
} }
func (c context) wrapErrs(errs ...error) error { func (c context) wrapErrs(errs ...error) error {

View File

@ -1892,6 +1892,7 @@ func (c configImpl) Concurrency() int {
type applyConfig struct { type applyConfig struct {
args string args string
values []string values []string
retainValuesFiles bool
set []string set []string
skipDeps bool skipDeps bool
suppressSecrets bool suppressSecrets bool
@ -1952,6 +1953,10 @@ func (a applyConfig) Logger() *zap.SugaredLogger {
return a.logger return a.logger
} }
func (a applyConfig) RetainValuesFiles() bool {
return a.retainValuesFiles
}
// Mocking the command-line runner // Mocking the command-line runner
type mockRunner struct { type mockRunner struct {

View File

@ -48,6 +48,8 @@ type ApplyConfigProvider interface {
NoColor() bool NoColor() bool
Context() int Context() int
RetainValuesFiles() bool
concurrencyConfig concurrencyConfig
interactive interactive
loggingConfig loggingConfig

View File

@ -9,6 +9,8 @@ type LoadOpts struct {
Selectors []string Selectors []string
Environment state.SubhelmfileEnvironmentSpec Environment state.SubhelmfileEnvironmentSpec
RetainValuesFiles bool
// CalleePath is the absolute path to the file being loaded // CalleePath is the absolute path to the file being loaded
CalleePath string CalleePath string
} }