diff --git a/pkg/app/app.go b/pkg/app/app.go index 2c8d5bfa..75e9dfed 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -158,15 +158,15 @@ func (a *App) Status(c StatusesConfigProvider) error { } func (a *App) Delete(c DeleteConfigProvider) error { - return a.reverse().ForEachStateFiltered(func(run *Run) []error { - return run.Delete(c) - }, true) + return a.reverse().ForEachState(func(run *Run) (bool, []error) { + return a.delete(run, c.Purge(), c) + }) } func (a *App) Destroy(c DestroyConfigProvider) error { - return a.reverse().ForEachStateFiltered(func(run *Run) []error { - return run.Destroy(c) - }, true) + return a.reverse().ForEachState(func(run *Run) (bool, []error) { + return a.delete(run, true, c) + }) } func (a *App) Test(c TestConfigProvider) error { @@ -399,13 +399,13 @@ func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*sta return nil } -func (a *App) ForEachStateFiltered(do func(*Run) []error, dagEnabled ...bool) error { +func (a *App) ForEachStateFiltered(do func(*Run) []error) error { ctx := NewContext() err := a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState, helm helmexec.Interface) []error { run := NewRun(st, helm, ctx) return do(run) - }, dagEnabled...) + }) if err != nil && a.ErrorHandler != nil { return a.ErrorHandler(err) @@ -460,7 +460,7 @@ func withDAG(templated *state.HelmState, helm helmexec.Interface, logger *zap.Su logger.Debugf("processing %d groups of releases in this order:\n%s", numBatches, printBatches(batches)) - all := true + any := false for i, batch := range batches { var targets []state.ReleaseSpec @@ -485,10 +485,10 @@ func withDAG(templated *state.HelmState, helm helmexec.Interface, logger *zap.Su return false, errs } - all = all && processed + any = any || processed } - return all, nil + return any, nil } type Opts struct { @@ -572,15 +572,9 @@ func (a *App) Wrap(converge func(*state.HelmState, helmexec.Interface) []error) } } -func (a *App) VisitDesiredStatesWithReleasesFiltered(fileOrDir string, converge func(*state.HelmState, helmexec.Interface) []error, dagEnabled ...bool) error { +func (a *App) VisitDesiredStatesWithReleasesFiltered(fileOrDir string, converge func(*state.HelmState, helmexec.Interface) []error) error { f := a.Wrap(converge) - if len(dagEnabled) > 0 && dagEnabled[0] { - return a.visitStatesWithSelectorsAndRemoteSupport(fileOrDir, func(st *state.HelmState, helm helmexec.Interface) (bool, []error) { - return withDAG(st, helm, a.Logger, a.Reverse, f) - }) - } - return a.visitStatesWithSelectorsAndRemoteSupport(fileOrDir, func(st *state.HelmState, helm helmexec.Interface) (bool, []error) { return f(st, helm) }) @@ -835,6 +829,78 @@ Do you really want to apply? return true, syncErrs } +func (a *App) delete(r *Run, purge bool, c DestroyConfigProvider) (bool, []error) { + st := r.state + helm := r.helm + + affectedReleases := state.AffectedReleases{} + + var deletingReleases []state.ReleaseSpec + + if len(st.Selectors) > 0 { + var err error + deletingReleases, err = st.GetFilteredReleases() + if err != nil { + return false, []error{err} + } + if len(deletingReleases) == 0 { + return false, nil + } + } else { + deletingReleases = st.Releases + } + + releasesToBeDeleted := map[string]state.ReleaseSpec{} + for _, r := range deletingReleases { + id := state.ReleaseToID(&r) + releasesToBeDeleted[id] = r + } + + names := make([]string, len(deletingReleases)) + for i, r := range deletingReleases { + names[i] = fmt.Sprintf(" %s (%s)", r.Name, r.Chart) + } + + var errs []error + var any bool + + msg := fmt.Sprintf(`Affected releases are: +%s + +Do you really want to delete? + Helmfile will delete all your releases, as shown above. + +`, strings.Join(names, "\n")) + interactive := c.Interactive() + if !interactive || interactive && r.askForConfirmation(msg) { + r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...) + + if len(releasesToBeDeleted) > 0 { + deleted, deletionErrs := withDAG(st, helm, a.Logger, true, a.Wrap(func(subst *state.HelmState, helm helmexec.Interface) []error { + var rs []state.ReleaseSpec + + for _, r := range subst.Releases { + if _, ok := releasesToBeDeleted[state.ReleaseToID(&r)]; ok { + rs = append(rs, r) + } + } + + subst.Releases = rs + + return subst.DeleteReleases(&affectedReleases, helm, c.Concurrency(), purge) + })) + + any = any || deleted + + if deletionErrs != nil && len(deletionErrs) > 0 { + errs = append(errs, deletionErrs...) + } + } + } + affectedReleases.DisplayAffectedReleases(c.Logger()) + return any, errs +} + func fileExistsAt(path string) bool { fileInfo, err := os.Stat(path) return err == nil && fileInfo.Mode().IsRegular() diff --git a/pkg/app/run.go b/pkg/app/run.go index 3a8bcbbf..6eeeae05 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -1,9 +1,6 @@ package app import ( - "fmt" - "strings" - "github.com/roboll/helmfile/pkg/argparser" "github.com/roboll/helmfile/pkg/helmexec" "github.com/roboll/helmfile/pkg/state" @@ -64,60 +61,6 @@ func (r *Run) Status(c StatusesConfigProvider) []error { return r.state.ReleaseStatuses(r.helm, workers) } -func (r *Run) Delete(c DeleteConfigProvider) []error { - affectedReleases := state.AffectedReleases{} - purge := c.Purge() - - errs := []error{} - - names := make([]string, len(r.state.Releases)) - for i, r := range r.state.Releases { - names[i] = fmt.Sprintf(" %s (%s)", r.Name, r.Chart) - } - - msg := fmt.Sprintf(`Affected releases are: -%s - -Do you really want to delete? - Helmfile will delete all your releases, as shown above. - -`, strings.Join(names, "\n")) - interactive := c.Interactive() - if !interactive || interactive && r.askForConfirmation(msg) { - r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...) - - errs = r.state.DeleteReleases(&affectedReleases, r.helm, c.Concurrency(), purge) - } - affectedReleases.DisplayAffectedReleases(c.Logger()) - return errs -} - -func (r *Run) Destroy(c DestroyConfigProvider) []error { - errs := []error{} - affectedReleases := state.AffectedReleases{} - - names := make([]string, len(r.state.Releases)) - for i, r := range r.state.Releases { - names[i] = fmt.Sprintf(" %s (%s)", r.Name, r.Chart) - } - - msg := fmt.Sprintf(`Affected releases are: -%s - -Do you really want to delete? - Helmfile will delete all your releases, as shown above. - -`, strings.Join(names, "\n")) - interactive := c.Interactive() - if !interactive || interactive && r.askForConfirmation(msg) { - r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...) - - errs = r.state.DeleteReleases(&affectedReleases, r.helm, c.Concurrency(), true) - } - affectedReleases.DisplayAffectedReleases(c.Logger()) - return errs -} - func (r *Run) Diff(c DiffConfigProvider) []error { st := r.state helm := r.helm