diff --git a/cmd/delete.go b/cmd/delete.go index c39c8890..441337c6 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -36,6 +36,7 @@ func NewDeleteCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.IntVar(&deleteOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited") f.BoolVar(&deleteOptions.Purge, "purge", false, "purge releases i.e. free release names and histories") f.BoolVar(&deleteOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) + f.BoolVar(&deleteOptions.SkipCharts, "skip-charts", false, "don't prepare charts when deleting releases") return cmd } diff --git a/cmd/destroy.go b/cmd/destroy.go index 8492e608..e53ea072 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -34,6 +34,7 @@ func NewDestroyCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm exec") f.IntVar(&destroyOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited") f.BoolVar(&destroyOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`) + f.BoolVar(&destroyOptions.SkipCharts, "skip-charts", false, "don't prepare charts when destroying releases") return cmd } diff --git a/docs/index.md b/docs/index.md index a7b9c74b..c2d4178a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -605,6 +605,7 @@ The `helmfile destroy` sub-command uninstalls and purges all the releases define `helmfile --interactive destroy` instructs Helmfile to request your confirmation before actually deleting releases. `destroy` basically runs `helm uninstall --purge` on all the targeted releases. If you don't want purging, use `helmfile delete` instead. +If `--skip-charts` flag is not set, destory would prepare all releases, by fetching charts and templating them. ### delete (DEPRECATED) @@ -613,6 +614,7 @@ The `helmfile delete` sub-command deletes all the releases defined in the manife `helmfile --interactive delete` instructs Helmfile to request your confirmation before actually deleting releases. Note that `delete` doesn't purge releases. So `helmfile delete && helmfile sync` results in sync failed due to that releases names are not deleted but preserved for future references. If you really want to remove releases for reuse, add `--purge` flag to run it like `helmfile delete --purge`. +If `--skip-charts` flag is not set, destory would prepare all releases, by fetching charts and templating them. ### secrets diff --git a/pkg/app/app.go b/pkg/app/app.go index b6942e96..f8f9a9e3 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -455,36 +455,41 @@ func (a *App) Status(c StatusesConfigProvider) error { // TODO: Remove this function once Helmfile v0.x func (a *App) Delete(c DeleteConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { - err := run.withPreparedCharts("delete", state.ChartPrepareOptions{ - SkipRepos: c.SkipDeps(), - SkipDeps: c.SkipDeps(), - Concurrency: c.Concurrency(), - }, func() { + if !c.SkipCharts() { + err := run.withPreparedCharts("delete", state.ChartPrepareOptions{ + SkipRepos: c.SkipDeps(), + SkipDeps: c.SkipDeps(), + Concurrency: c.Concurrency(), + }, func() { + ok, errs = a.delete(run, c.Purge(), c) + }) + + if err != nil { + errs = append(errs, err) + } + } else { ok, errs = a.delete(run, c.Purge(), c) - }) - - if err != nil { - errs = append(errs, err) } - return }, false, SetReverse(true)) } func (a *App) Destroy(c DestroyConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { - err := run.withPreparedCharts("destroy", state.ChartPrepareOptions{ - SkipRepos: c.SkipDeps(), - SkipDeps: c.SkipDeps(), - Concurrency: c.Concurrency(), - }, func() { + if !c.SkipCharts() { + err := run.withPreparedCharts("destroy", state.ChartPrepareOptions{ + SkipRepos: c.SkipDeps(), + SkipDeps: c.SkipDeps(), + Concurrency: c.Concurrency(), + }, func() { + ok, errs = a.delete(run, true, c) + }) + if err != nil { + errs = append(errs, err) + } + } else { ok, errs = a.delete(run, true, c) - }) - - if err != nil { - errs = append(errs, err) } - return }, false, SetReverse(true)) } diff --git a/pkg/app/config.go b/pkg/app/config.go index 0204829d..7da50308 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -142,6 +142,7 @@ type DeleteConfigProvider interface { Purge() bool SkipDeps() bool + SkipCharts() bool interactive loggingConfig @@ -152,6 +153,7 @@ type DestroyConfigProvider interface { Args() string SkipDeps() bool + SkipCharts() bool interactive loggingConfig diff --git a/pkg/app/destroy_test.go b/pkg/app/destroy_test.go index 13e290c4..098cad20 100644 --- a/pkg/app/destroy_test.go +++ b/pkg/app/destroy_test.go @@ -39,12 +39,17 @@ type destroyConfig struct { skipDeps bool logger *zap.SugaredLogger includeTransitiveNeeds bool + skipCharts bool } func (d destroyConfig) Args() string { return d.args } +func (d destroyConfig) SkipCharts() bool { + return d.skipCharts +} + func (d destroyConfig) Interactive() bool { return d.interactive } diff --git a/pkg/config/delete.go b/pkg/config/delete.go index 8b58a96a..41fe177b 100644 --- a/pkg/config/delete.go +++ b/pkg/config/delete.go @@ -9,6 +9,8 @@ type DeleteOptions struct { Purge bool // SkipDeps is the skip deps flag SkipDeps bool + // SkipCharts makes Delete skip `withPreparedCharts` + SkipCharts bool } // NewDeleteOptions creates a new Apply @@ -44,3 +46,8 @@ func (c *DeleteImpl) Purge() bool { func (c *DeleteImpl) SkipDeps() bool { return c.DeleteOptions.SkipDeps } + +// SkipCharts returns skipCharts flag +func (c *DeleteImpl) SkipCharts() bool { + return c.DeleteOptions.SkipCharts +} diff --git a/pkg/config/destroy.go b/pkg/config/destroy.go index e4878ed8..71e9f3b4 100644 --- a/pkg/config/destroy.go +++ b/pkg/config/destroy.go @@ -6,6 +6,8 @@ type DestroyOptions struct { Concurrency int // SkipDeps is the skip deps flag SkipDeps bool + // SkipCharts makes Destroy skip `withPreparedCharts` + SkipCharts bool } // NewDestroyOptions creates a new Apply @@ -36,3 +38,8 @@ func (c *DestroyImpl) Concurrency() int { func (c *DestroyImpl) SkipDeps() bool { return c.DestroyOptions.SkipDeps } + +// SkipCharts returns skipCharts flag +func (c *DestroyImpl) SkipCharts() bool { + return c.DestroyOptions.SkipCharts +} diff --git a/test/integration/test-cases/happypath.sh b/test/integration/test-cases/happypath.sh index 3befe66a..1af9f876 100644 --- a/test/integration/test-cases/happypath.sh +++ b/test/integration/test-cases/happypath.sh @@ -70,6 +70,19 @@ ${helm} status --namespace=${test_ns} httpbin &> /dev/null && fail "release shou info "Ensuring \"helmfile destroy\" doesn't fail when no releases installed" ${helmfile} -f ${happypath_case_input_dir}/${config_file} destroy || fail "\"helmfile delete\" shouldn't fail when there are no installed releases" +info "Re-applying ${happypath_case_input_dir}/${config_file} with locked dependencies" +${helmfile} -f ${happypath_case_input_dir}/${config_file} apply +code=$? +[ ${code} -eq 0 ] || fail "unexpected exit code returned by helmfile apply: ${code}" +${helm} list --namespace=${test_ns} || fail "unable to list releases" + +info "Deleting release with --skip-charts" +${helmfile} -f ${happypath_case_input_dir}/${config_file} destroy --skip-charts +${helm} status --namespace=${test_ns} httpbin &> /dev/null && fail "release should not exist anymore after a delete" + +info "Ensuring \"helmfile destroy --skip-charts\" doesn't fail when no releases installed" +${helmfile} -f ${happypath_case_input_dir}/${config_file} destroy --skip-charts || fail "\"helmfile delete\" shouldn't fail when there are no installed releases" + info "Ensuring \"helmfile template\" output does contain only YAML docs" (${helmfile} -f ${happypath_case_input_dir}/${config_file} template | kubectl apply -f -) || fail "\"helmfile template | kubectl apply -f -\" shouldn't fail"