Merge pull request #13629 from gjenkins8/rename_atomic_rollbackonfailure

Rename 'atomic' -> 'rollback-on-failure'
pull/31178/head
George Jenkins 2 weeks ago committed by GitHub
commit a0d6b0d383
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -95,7 +95,8 @@ type Install struct {
NameTemplate string NameTemplate string
Description string Description string
OutputDir string OutputDir string
Atomic bool // RollbackOnFailure enables rolling back (uninstalling) the release on failure if set
RollbackOnFailure bool
SkipCRDs bool SkipCRDs bool
SubNotes bool SubNotes bool
HideNotes bool HideNotes bool
@ -298,9 +299,9 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
slog.Debug("API Version list given outside of client only mode, this list will be ignored") slog.Debug("API Version list given outside of client only mode, this list will be ignored")
} }
// Make sure if Atomic is set, that wait is set as well. This makes it so // Make sure if RollbackOnFailure is set, that wait is set as well. This makes it so
// the user doesn't have to specify both // the user doesn't have to specify both
if i.WaitStrategy == kube.HookOnlyStrategy && i.Atomic { if i.WaitStrategy == kube.HookOnlyStrategy && i.RollbackOnFailure {
i.WaitStrategy = kube.StatusWatcherStrategy i.WaitStrategy = kube.StatusWatcherStrategy
} }
@ -530,8 +531,8 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
func (i *Install) failRelease(rel *release.Release, err error) (*release.Release, error) { func (i *Install) failRelease(rel *release.Release, err error) (*release.Release, error) {
rel.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", i.ReleaseName, err.Error())) rel.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", i.ReleaseName, err.Error()))
if i.Atomic { if i.RollbackOnFailure {
slog.Debug("install failed, uninstalling release", "release", i.ReleaseName) slog.Debug("install failed and rollback-on-failure is set, uninstalling release", "release", i.ReleaseName)
uninstall := NewUninstall(i.cfg) uninstall := NewUninstall(i.cfg)
uninstall.DisableHooks = i.DisableHooks uninstall.DisableHooks = i.DisableHooks
uninstall.KeepHistory = false uninstall.KeepHistory = false
@ -539,7 +540,7 @@ func (i *Install) failRelease(rel *release.Release, err error) (*release.Release
if _, uninstallErr := uninstall.Run(i.ReleaseName); uninstallErr != nil { if _, uninstallErr := uninstall.Run(i.ReleaseName); uninstallErr != nil {
return rel, fmt.Errorf("an error occurred while uninstalling the release. original install error: %w: %w", err, uninstallErr) return rel, fmt.Errorf("an error occurred while uninstalling the release. original install error: %w: %w", err, uninstallErr)
} }
return rel, fmt.Errorf("release %s failed, and has been uninstalled due to atomic being set: %w", i.ReleaseName, err) return rel, fmt.Errorf("release %s failed, and has been uninstalled due to rollback-on-failure being set: %w", i.ReleaseName, err)
} }
i.recordRelease(rel) // Ignore the error, since we have another error to deal with. i.recordRelease(rel) // Ignore the error, since we have another error to deal with.
return rel, err return rel, err

@ -590,16 +590,16 @@ func TestInstallRelease_WaitForJobs(t *testing.T) {
is.Equal(res.Info.Status, release.StatusFailed) is.Equal(res.Info.Status, release.StatusFailed)
} }
func TestInstallRelease_Atomic(t *testing.T) { func TestInstallRelease_RollbackOnFailure(t *testing.T) {
is := assert.New(t) is := assert.New(t)
t.Run("atomic uninstall succeeds", func(t *testing.T) { t.Run("rollback-on-failure uninstall succeeds", func(t *testing.T) {
instAction := installAction(t) instAction := installAction(t)
instAction.ReleaseName = "come-fail-away" instAction.ReleaseName = "come-fail-away"
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitError = fmt.Errorf("I timed out") failer.WaitError = fmt.Errorf("I timed out")
instAction.cfg.KubeClient = failer instAction.cfg.KubeClient = failer
instAction.Atomic = true instAction.RollbackOnFailure = true
// disabling hooks to avoid an early fail when // disabling hooks to avoid an early fail when
// WaitForDelete is called on the pre-delete hook execution // WaitForDelete is called on the pre-delete hook execution
instAction.DisableHooks = true instAction.DisableHooks = true
@ -608,7 +608,7 @@ func TestInstallRelease_Atomic(t *testing.T) {
res, err := instAction.Run(buildChart(), vals) res, err := instAction.Run(buildChart(), vals)
is.Error(err) is.Error(err)
is.Contains(err.Error(), "I timed out") is.Contains(err.Error(), "I timed out")
is.Contains(err.Error(), "atomic") is.Contains(err.Error(), "rollback-on-failure")
// Now make sure it isn't in storage anymore // Now make sure it isn't in storage anymore
_, err = instAction.cfg.Releases.Get(res.Name, res.Version) _, err = instAction.cfg.Releases.Get(res.Name, res.Version)
@ -616,14 +616,14 @@ func TestInstallRelease_Atomic(t *testing.T) {
is.Equal(err, driver.ErrReleaseNotFound) is.Equal(err, driver.ErrReleaseNotFound)
}) })
t.Run("atomic uninstall fails", func(t *testing.T) { t.Run("rollback-on-failure uninstall fails", func(t *testing.T) {
instAction := installAction(t) instAction := installAction(t)
instAction.ReleaseName = "come-fail-away-with-me" instAction.ReleaseName = "come-fail-away-with-me"
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitError = fmt.Errorf("I timed out") failer.WaitError = fmt.Errorf("I timed out")
failer.DeleteError = fmt.Errorf("uninstall fail") failer.DeleteError = fmt.Errorf("uninstall fail")
instAction.cfg.KubeClient = failer instAction.cfg.KubeClient = failer
instAction.Atomic = true instAction.RollbackOnFailure = true
vals := map[string]interface{}{} vals := map[string]interface{}{}
_, err := instAction.Run(buildChart(), vals) _, err := instAction.Run(buildChart(), vals)
@ -633,7 +633,7 @@ func TestInstallRelease_Atomic(t *testing.T) {
is.Contains(err.Error(), "an error occurred while uninstalling the release") is.Contains(err.Error(), "an error occurred while uninstalling the release")
}) })
} }
func TestInstallRelease_Atomic_Interrupted(t *testing.T) { func TestInstallRelease_RollbackOnFailure_Interrupted(t *testing.T) {
is := assert.New(t) is := assert.New(t)
instAction := installAction(t) instAction := installAction(t)
@ -641,7 +641,7 @@ func TestInstallRelease_Atomic_Interrupted(t *testing.T) {
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitDuration = 10 * time.Second failer.WaitDuration = 10 * time.Second
instAction.cfg.KubeClient = failer instAction.cfg.KubeClient = failer
instAction.Atomic = true instAction.RollbackOnFailure = true
vals := map[string]interface{}{} vals := map[string]interface{}{}
ctx, cancel := context.WithCancel(t.Context()) ctx, cancel := context.WithCancel(t.Context())
@ -652,7 +652,7 @@ func TestInstallRelease_Atomic_Interrupted(t *testing.T) {
res, err := instAction.RunWithContext(ctx, buildChart(), vals) res, err := instAction.RunWithContext(ctx, buildChart(), vals)
is.Error(err) is.Error(err)
is.Contains(err.Error(), "context canceled") is.Contains(err.Error(), "context canceled")
is.Contains(err.Error(), "atomic") is.Contains(err.Error(), "rollback-on-failure")
is.Contains(err.Error(), "uninstalled") is.Contains(err.Error(), "uninstalled")
// Now make sure it isn't in storage anymore // Now make sure it isn't in storage anymore

@ -89,8 +89,8 @@ type Upgrade struct {
ResetThenReuseValues bool ResetThenReuseValues bool
// MaxHistory limits the maximum number of revisions saved per release // MaxHistory limits the maximum number of revisions saved per release
MaxHistory int MaxHistory int
// Atomic, if true, will roll back on failure. // RollbackOnFailure enables rolling back the upgraded release on failure
Atomic bool RollbackOnFailure bool
// CleanupOnFail will, if true, cause the upgrade to delete newly-created resources on a failed update. // CleanupOnFail will, if true, cause the upgrade to delete newly-created resources on a failed update.
CleanupOnFail bool CleanupOnFail bool
// SubNotes determines whether sub-notes are rendered in the chart. // SubNotes determines whether sub-notes are rendered in the chart.
@ -151,9 +151,9 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
return nil, err return nil, err
} }
// Make sure if Atomic is set, that wait is set as well. This makes it so // Make sure wait is set if RollbackOnFailure. This makes it so
// the user doesn't have to specify both // the user doesn't have to specify both
if u.WaitStrategy == kube.HookOnlyStrategy && u.Atomic { if u.WaitStrategy == kube.HookOnlyStrategy && u.RollbackOnFailure {
u.WaitStrategy = kube.StatusWatcherStrategy u.WaitStrategy = kube.StatusWatcherStrategy
} }
@ -390,7 +390,7 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
} }
} }
// Function used to lock the Mutex, this is important for the case when the atomic flag is set. // Function used to lock the Mutex, this is important for the case when RollbackOnFailure is set.
// In that case the upgrade will finish before the rollback is finished so it is necessary to wait for the rollback to finish. // In that case the upgrade will finish before the rollback is finished so it is necessary to wait for the rollback to finish.
// The rollback will be trigger by the function failRelease // The rollback will be trigger by the function failRelease
func (u *Upgrade) reportToPerformUpgrade(c chan<- resultMessage, rel *release.Release, created kube.ResourceList, err error) { func (u *Upgrade) reportToPerformUpgrade(c chan<- resultMessage, rel *release.Release, created kube.ResourceList, err error) {
@ -408,7 +408,7 @@ func (u *Upgrade) handleContext(ctx context.Context, done chan interface{}, c ch
case <-ctx.Done(): case <-ctx.Done():
err := ctx.Err() err := ctx.Err()
// when the atomic flag is set the ongoing release finish first and doesn't give time for the rollback happens. // when RollbackOnFailure is set, the ongoing release finish first and doesn't give time for the rollback happens.
u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err) u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, err)
case <-done: case <-done:
return return
@ -499,8 +499,9 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
} }
slog.Debug("resource cleanup complete") slog.Debug("resource cleanup complete")
} }
if u.Atomic {
slog.Debug("upgrade failed and atomic is set, rolling back to last successful release") if u.RollbackOnFailure {
slog.Debug("Upgrade failed and rollback-on-failure is set, rolling back to previous successful release")
// As a protection, get the last successful release before rollback. // As a protection, get the last successful release before rollback.
// If there are no successful releases, bail out // If there are no successful releases, bail out
@ -534,7 +535,7 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
if rollErr := rollin.Run(rel.Name); rollErr != nil { if rollErr := rollin.Run(rel.Name); rollErr != nil {
return rel, fmt.Errorf("an error occurred while rolling back the release. original upgrade error: %w: %w", err, rollErr) return rel, fmt.Errorf("an error occurred while rolling back the release. original upgrade error: %w: %w", err, rollErr)
} }
return rel, fmt.Errorf("release %s failed, and has been rolled back due to atomic being set: %w", rel.Name, err) return rel, fmt.Errorf("release %s failed, and has been rolled back due to rollback-on-failure being set: %w", rel.Name, err)
} }
return rel, err return rel, err

@ -141,11 +141,11 @@ func TestUpgradeRelease_CleanupOnFail(t *testing.T) {
is.Equal(res.Info.Status, release.StatusFailed) is.Equal(res.Info.Status, release.StatusFailed)
} }
func TestUpgradeRelease_Atomic(t *testing.T) { func TestUpgradeRelease_RollbackOnFailure(t *testing.T) {
is := assert.New(t) is := assert.New(t)
req := require.New(t) req := require.New(t)
t.Run("atomic rollback succeeds", func(t *testing.T) { t.Run("rollback-on-failure rollback succeeds", func(t *testing.T) {
upAction := upgradeAction(t) upAction := upgradeAction(t)
rel := releaseStub() rel := releaseStub()
@ -157,13 +157,13 @@ func TestUpgradeRelease_Atomic(t *testing.T) {
// We can't make Update error because then the rollback won't work // We can't make Update error because then the rollback won't work
failer.WatchUntilReadyError = fmt.Errorf("arming key removed") failer.WatchUntilReadyError = fmt.Errorf("arming key removed")
upAction.cfg.KubeClient = failer upAction.cfg.KubeClient = failer
upAction.Atomic = true upAction.RollbackOnFailure = true
vals := map[string]interface{}{} vals := map[string]interface{}{}
res, err := upAction.Run(rel.Name, buildChart(), vals) res, err := upAction.Run(rel.Name, buildChart(), vals)
req.Error(err) req.Error(err)
is.Contains(err.Error(), "arming key removed") is.Contains(err.Error(), "arming key removed")
is.Contains(err.Error(), "atomic") is.Contains(err.Error(), "rollback-on-failure")
// Now make sure it is actually upgraded // Now make sure it is actually upgraded
updatedRes, err := upAction.cfg.Releases.Get(res.Name, 3) updatedRes, err := upAction.cfg.Releases.Get(res.Name, 3)
@ -172,7 +172,7 @@ func TestUpgradeRelease_Atomic(t *testing.T) {
is.Equal(updatedRes.Info.Status, release.StatusDeployed) is.Equal(updatedRes.Info.Status, release.StatusDeployed)
}) })
t.Run("atomic uninstall fails", func(t *testing.T) { t.Run("rollback-on-failure uninstall fails", func(t *testing.T) {
upAction := upgradeAction(t) upAction := upgradeAction(t)
rel := releaseStub() rel := releaseStub()
rel.Name = "fallout" rel.Name = "fallout"
@ -182,7 +182,7 @@ func TestUpgradeRelease_Atomic(t *testing.T) {
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient) failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.UpdateError = fmt.Errorf("update fail") failer.UpdateError = fmt.Errorf("update fail")
upAction.cfg.KubeClient = failer upAction.cfg.KubeClient = failer
upAction.Atomic = true upAction.RollbackOnFailure = true
vals := map[string]interface{}{} vals := map[string]interface{}{}
_, err := upAction.Run(rel.Name, buildChart(), vals) _, err := upAction.Run(rel.Name, buildChart(), vals)
@ -409,7 +409,8 @@ func TestUpgradeRelease_Interrupted_Wait(t *testing.T) {
is.Equal(res.Info.Status, release.StatusFailed) is.Equal(res.Info.Status, release.StatusFailed)
} }
func TestUpgradeRelease_Interrupted_Atomic(t *testing.T) { func TestUpgradeRelease_Interrupted_RollbackOnFailure(t *testing.T) {
is := assert.New(t) is := assert.New(t)
req := require.New(t) req := require.New(t)
@ -422,7 +423,7 @@ func TestUpgradeRelease_Interrupted_Atomic(t *testing.T) {
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient) failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitDuration = 5 * time.Second failer.WaitDuration = 5 * time.Second
upAction.cfg.KubeClient = failer upAction.cfg.KubeClient = failer
upAction.Atomic = true upAction.RollbackOnFailure = true
vals := map[string]interface{}{} vals := map[string]interface{}{}
ctx, cancel := context.WithCancel(t.Context()) ctx, cancel := context.WithCancel(t.Context())
@ -431,7 +432,7 @@ func TestUpgradeRelease_Interrupted_Atomic(t *testing.T) {
res, err := upAction.RunWithContext(ctx, rel.Name, buildChart(), vals) res, err := upAction.RunWithContext(ctx, rel.Name, buildChart(), vals)
req.Error(err) req.Error(err)
is.Contains(err.Error(), "release interrupted-release failed, and has been rolled back due to atomic being set: context canceled") is.Contains(err.Error(), "release interrupted-release failed, and has been rolled back due to rollback-on-failure being set: context canceled")
// Now make sure it is actually upgraded // Now make sure it is actually upgraded
updatedRes, err := upAction.cfg.Releases.Get(res.Name, 3) updatedRes, err := upAction.cfg.Releases.Get(res.Name, 3)

@ -206,7 +206,8 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
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.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema") f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically to \"watcher\" if --atomic is used") f.BoolVar(&client.RollbackOnFailure, "rollback-on-failure", false, "if set, Helm will rollback (uninstall) the installation upon failure. The --wait flag will be default to \"watcher\" if --rollback-on-failure is set")
f.MarkDeprecated("atomic", "use --rollback-on-failure instead")
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.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation") f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")

@ -140,7 +140,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
instClient.WaitForJobs = client.WaitForJobs instClient.WaitForJobs = client.WaitForJobs
instClient.Devel = client.Devel instClient.Devel = client.Devel
instClient.Namespace = client.Namespace instClient.Namespace = client.Namespace
instClient.Atomic = client.Atomic instClient.RollbackOnFailure = client.RollbackOnFailure
instClient.PostRenderer = client.PostRenderer instClient.PostRenderer = client.PostRenderer
instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation
instClient.SubNotes = client.SubNotes instClient.SubNotes = client.SubNotes
@ -281,7 +281,9 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored") f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored")
f.BoolVar(&client.ResetThenReuseValues, "reset-then-reuse-values", false, "when upgrading, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' or '--reuse-values' is specified, this is ignored") f.BoolVar(&client.ResetThenReuseValues, "reset-then-reuse-values", false, "when upgrading, reset the values to the ones built into the chart, apply the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' or '--reuse-values' is specified, this is ignored")
f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically to \"watcher\" if --atomic is used") f.BoolVar(&client.RollbackOnFailure, "rollback-on-failure", false, "if set, Helm will rollback the upgrade to previous success release upon failure. The --wait flag will be defaulted to \"watcher\" if --rollback-on-failure is set")
f.BoolVar(&client.RollbackOnFailure, "atomic", false, "deprecated")
f.MarkDeprecated("atomic", "use --rollback-on-failure instead")
f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit") f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails") f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
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")

Loading…
Cancel
Save