cmd,pkg: Add a flag which when set will make helm wait for hook

deletions to complete

Signed-off-by: David Buchmann <david.buchmann@gmail.com>
pull/10584/head
David Buchmann 4 years ago
parent 86a94f2f53
commit c9e03bd881
No known key found for this signature in database
GPG Key ID: 610244B6AD4C19AE

@ -155,6 +155,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used") f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.BoolVar(&client.SyncDeleteHook, "sync-delete-hook", false, "make hooks that perform a delete wait for resources to be deleted")
addValueOptionsFlags(f, valueOpts) addValueOptionsFlags(f, valueOpts)
addChartPathOptionsFlags(f, &client.ChartPathOptions) addChartPathOptionsFlags(f, &client.ChartPathOptions)

@ -229,6 +229,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.StringVar(&client.Description, "description", "", "add a custom description") f.StringVar(&client.Description, "description", "", "add a custom description")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
f.BoolVar(&client.SyncDeleteHook, "sync-delete-hook", false, "make hooks that perform a delete wait for resources to be deleted")
addChartPathOptionsFlags(f, &client.ChartPathOptions) addChartPathOptionsFlags(f, &client.ChartPathOptions)
addValueOptionsFlags(f, valueOpts) addValueOptionsFlags(f, valueOpts)
bindOutputFlag(cmd, &outfmt) bindOutputFlag(cmd, &outfmt)

@ -22,12 +22,13 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"helm.sh/helm/v3/pkg/kube"
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
helmtime "helm.sh/helm/v3/pkg/time" helmtime "helm.sh/helm/v3/pkg/time"
) )
// execHook executes all of the hooks for the given hook event. // execHook executes all of the hooks for the given hook event.
func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, timeout time.Duration) error { func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, timeout time.Duration, syncDeleteHook bool) error {
executingHooks := []*release.Hook{} executingHooks := []*release.Hook{}
for _, h := range rl.Hooks { for _, h := range rl.Hooks {
@ -51,7 +52,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
h.DeletePolicies = []release.HookDeletePolicy{release.HookBeforeHookCreation} h.DeletePolicies = []release.HookDeletePolicy{release.HookBeforeHookCreation}
} }
if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation); err != nil { if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation, syncDeleteHook, timeout); err != nil {
return err return err
} }
@ -88,7 +89,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
h.LastRun.Phase = release.HookPhaseFailed h.LastRun.Phase = release.HookPhaseFailed
// If a hook is failed, check the annotation of the hook to determine whether the hook should be deleted // If a hook is failed, check the annotation of the hook to determine whether the hook should be deleted
// under failed condition. If so, then clear the corresponding resource object in the hook // under failed condition. If so, then clear the corresponding resource object in the hook
if err := cfg.deleteHookByPolicy(h, release.HookFailed); err != nil { if err := cfg.deleteHookByPolicy(h, release.HookFailed, syncDeleteHook, timeout); err != nil {
return err return err
} }
return err return err
@ -99,7 +100,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
// If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted // If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted
// under succeeded condition. If so, then clear the corresponding resource object in each hook // under succeeded condition. If so, then clear the corresponding resource object in each hook
for _, h := range executingHooks { for _, h := range executingHooks {
if err := cfg.deleteHookByPolicy(h, release.HookSucceeded); err != nil { if err := cfg.deleteHookByPolicy(h, release.HookSucceeded, syncDeleteHook, timeout); err != nil {
return err return err
} }
} }
@ -120,7 +121,7 @@ func (x hookByWeight) Less(i, j int) bool {
} }
// deleteHookByPolicy deletes a hook if the hook policy instructs it to // deleteHookByPolicy deletes a hook if the hook policy instructs it to
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error { func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy, syncDeleteHook bool, deleteTimeout time.Duration) error {
// Never delete CustomResourceDefinitions; this could cause lots of // Never delete CustomResourceDefinitions; this could cause lots of
// cascading garbage collection. // cascading garbage collection.
if h.Kind == "CustomResourceDefinition" { if h.Kind == "CustomResourceDefinition" {
@ -135,6 +136,13 @@ func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.Hoo
if len(errs) > 0 { if len(errs) > 0 {
return errors.New(joinErrors(errs)) return errors.New(joinErrors(errs))
} }
if syncDeleteHook {
if kubeClient, ok := cfg.KubeClient.(kube.InterfaceExt); ok {
if err := kubeClient.WaitForDelete(resources, deleteTimeout); err != nil {
return err
}
}
}
} }
return nil return nil
} }

@ -100,6 +100,8 @@ type Install struct {
// OutputDir/<ReleaseName> // OutputDir/<ReleaseName>
UseReleaseName bool UseReleaseName bool
PostRenderer postrender.PostRenderer PostRenderer postrender.PostRenderer
// Make deletion hooks wait for completion
SyncDeleteHook bool
// Lock to control raceconditions when the process receives a SIGTERM // Lock to control raceconditions when the process receives a SIGTERM
Lock sync.Mutex Lock sync.Mutex
} }
@ -355,7 +357,7 @@ func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, t
// pre-install hooks // pre-install hooks
if !i.DisableHooks { if !i.DisableHooks {
if err := i.cfg.execHook(rel, release.HookPreInstall, i.Timeout); err != nil { if err := i.cfg.execHook(rel, release.HookPreInstall, i.Timeout, i.SyncDeleteHook); err != nil {
i.reportToRun(c, rel, fmt.Errorf("failed pre-install: %s", err)) i.reportToRun(c, rel, fmt.Errorf("failed pre-install: %s", err))
return return
} }
@ -391,7 +393,7 @@ func (i *Install) performInstall(c chan<- resultMessage, rel *release.Release, t
} }
if !i.DisableHooks { if !i.DisableHooks {
if err := i.cfg.execHook(rel, release.HookPostInstall, i.Timeout); err != nil { if err := i.cfg.execHook(rel, release.HookPostInstall, i.Timeout, i.SyncDeleteHook); err != nil {
i.reportToRun(c, rel, fmt.Errorf("failed post-install: %s", err)) i.reportToRun(c, rel, fmt.Errorf("failed post-install: %s", err))
return return
} }

@ -88,7 +88,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
rel.Hooks = executingHooks rel.Hooks = executingHooks
} }
if err := r.cfg.execHook(rel, release.HookTest, r.Timeout); err != nil { if err := r.cfg.execHook(rel, release.HookTest, r.Timeout, false); err != nil {
rel.Hooks = append(skippedHooks, rel.Hooks...) rel.Hooks = append(skippedHooks, rel.Hooks...)
r.cfg.Releases.Update(rel) r.cfg.Releases.Update(rel)
return rel, err return rel, err

@ -45,6 +45,8 @@ type Rollback struct {
Force bool // will (if true) force resource upgrade through uninstall/recreate if needed Force bool // will (if true) force resource upgrade through uninstall/recreate if needed
CleanupOnFail bool CleanupOnFail bool
MaxHistory int // MaxHistory limits the maximum number of revisions saved per release MaxHistory int // MaxHistory limits the maximum number of revisions saved per release
// Make deletion hooks wait for completion
SyncDeleteHook bool
} }
// NewRollback creates a new Rollback object with the given configuration. // NewRollback creates a new Rollback object with the given configuration.
@ -157,7 +159,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
// pre-rollback hooks // pre-rollback hooks
if !r.DisableHooks { if !r.DisableHooks {
if err := r.cfg.execHook(targetRelease, release.HookPreRollback, r.Timeout); err != nil { if err := r.cfg.execHook(targetRelease, release.HookPreRollback, r.Timeout, r.SyncDeleteHook); err != nil {
return targetRelease, err return targetRelease, err
} }
} else { } else {
@ -219,7 +221,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
// post-rollback hooks // post-rollback hooks
if !r.DisableHooks { if !r.DisableHooks {
if err := r.cfg.execHook(targetRelease, release.HookPostRollback, r.Timeout); err != nil { if err := r.cfg.execHook(targetRelease, release.HookPostRollback, r.Timeout, r.SyncDeleteHook); err != nil {
return targetRelease, err return targetRelease, err
} }
} }

@ -99,7 +99,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
res := &release.UninstallReleaseResponse{Release: rel} res := &release.UninstallReleaseResponse{Release: rel}
if !u.DisableHooks { if !u.DisableHooks {
if err := u.cfg.execHook(rel, release.HookPreDelete, u.Timeout); err != nil { if err := u.cfg.execHook(rel, release.HookPreDelete, u.Timeout, false); err != nil {
return res, err return res, err
} }
} else { } else {
@ -128,7 +128,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
} }
if !u.DisableHooks { if !u.DisableHooks {
if err := u.cfg.execHook(rel, release.HookPostDelete, u.Timeout); err != nil { if err := u.cfg.execHook(rel, release.HookPostDelete, u.Timeout, false); err != nil {
errs = append(errs, err) errs = append(errs, err)
} }
} }

@ -101,6 +101,8 @@ type Upgrade struct {
DisableOpenAPIValidation bool DisableOpenAPIValidation bool
// Get missing dependencies // Get missing dependencies
DependencyUpdate bool DependencyUpdate bool
// Make deletion hooks wait for completion
SyncDeleteHook bool
// Lock to control raceconditions when the process receives a SIGTERM // Lock to control raceconditions when the process receives a SIGTERM
Lock sync.Mutex Lock sync.Mutex
} }
@ -367,7 +369,7 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
// pre-upgrade hooks // pre-upgrade hooks
if !u.DisableHooks { if !u.DisableHooks {
if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.Timeout); err != nil { if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.Timeout, u.SyncDeleteHook); err != nil {
u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err)) u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err))
return return
} }
@ -410,7 +412,7 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
// post-upgrade hooks // post-upgrade hooks
if !u.DisableHooks { if !u.DisableHooks {
if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.Timeout); err != nil { if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.Timeout, u.SyncDeleteHook); err != nil {
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err)) u.reportToPerformUpgrade(c, upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err))
return return
} }
@ -478,6 +480,7 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
rollin.Recreate = u.Recreate rollin.Recreate = u.Recreate
rollin.Force = u.Force rollin.Force = u.Force
rollin.Timeout = u.Timeout rollin.Timeout = u.Timeout
rollin.SyncDeleteHook = u.SyncDeleteHook
if rollErr := rollin.Run(rel.Name); rollErr != nil { if rollErr := rollin.Run(rel.Name); rollErr != nil {
return rel, errors.Wrapf(rollErr, "an error occurred while rolling back the release. original upgrade error: %s", err) return rel, errors.Wrapf(rollErr, "an error occurred while rolling back the release. original upgrade error: %s", err)
} }

Loading…
Cancel
Save