Skip to content

Commit

Permalink
feat(helm): add --reset-then-reuse-values flag to 'helm upgrade'
Browse files Browse the repository at this point in the history
When '--reset-then-reuse-values' is used on 'helm upgrade', the chart's values will be
reset to the values of the deployed chart while the current release's values will be
reused and merged with the values passed as argument (is any). '--reset-values' and
'--reuse-values' flags take precedence over `--reset-then-reuse-values', making it
ignored if one or the other is also used.

Closes #8085, #3957

Signed-off-by: Quentin Devos <quentin@devos.pm>
  • Loading branch information
Okhoshi committed Apr 27, 2021
1 parent 43853ea commit a9d59f9
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/helm/upgrade.go
Expand Up @@ -179,6 +179,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
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.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state 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 if --atomic is used")
Expand Down
11 changes: 11 additions & 0 deletions pkg/action/upgrade.go
Expand Up @@ -79,6 +79,8 @@ type Upgrade struct {
ResetValues bool
// ReuseValues will re-use the user's last supplied values.
ReuseValues bool
// ResetThenReuseValues will reset the values to the chart's built-ins then merge with user's last supplied values.
ResetThenReuseValues bool
// Recreate will (if true) recreate pods after a rollback.
Recreate bool
// MaxHistory limits the maximum number of revisions saved per release
Expand Down Expand Up @@ -455,6 +457,15 @@ func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newV
return newVals, nil
}

// If the ResetThenReuseValues flag is set, we use the new chart's values, but we copy the old config's values over the new config's values.
if u.ResetThenReuseValues {
u.cfg.Log("merging values from old release to new values")

newVals = chartutil.CoalesceTables(newVals, current.Config)

return newVals, nil
}

if len(newVals) == 0 && len(current.Config) > 0 {
u.cfg.Log("copying values from %s (v%d) to new release.", current.Name, current.Version)
newVals = current.Config
Expand Down
53 changes: 53 additions & 0 deletions pkg/action/upgrade_test.go
Expand Up @@ -277,6 +277,59 @@ func TestUpgradeRelease_ReuseValues(t *testing.T) {
})
}

func TestUpgradeRelease_ResetThenReuseValues(t *testing.T) {
is := assert.New(t)

t.Run("reset then reuse values should work with values", func(t *testing.T) {
upAction := upgradeAction(t)

existingValues := map[string]interface{}{
"name": "value",
"maxHeapSize": "128m",
"replicas": 2,
}
newValues := map[string]interface{}{
"name": "newValue",
"maxHeapSize": "512m",
"cpu": "12m",
}
newChartValues := map[string]interface{}{
"memory": "256m",
}
expectedValues := map[string]interface{}{
"name": "newValue",
"maxHeapSize": "512m",
"cpu": "12m",
"replicas": 2,
}

rel := releaseStub()
rel.Name = "nuketown"
rel.Info.Status = release.StatusDeployed
rel.Config = existingValues

err := upAction.cfg.Releases.Create(rel)
is.NoError(err)

upAction.ResetThenReuseValues = true
// setting newValues and upgrading
res, err := upAction.Run(rel.Name, buildChart(withValues(newChartValues)), newValues)
is.NoError(err)

// Now make sure it is actually upgraded
updatedRes, err := upAction.cfg.Releases.Get(res.Name, 2)
is.NoError(err)

if updatedRes == nil {
is.Fail("Updated Release is nil")
return
}
is.Equal(release.StatusDeployed, updatedRes.Info.Status)
is.Equal(expectedValues, updatedRes.Config)
is.Equal(newChartValues, updatedRes.Chart.Values)
})
}

func TestUpgradeRelease_Pending(t *testing.T) {
req := require.New(t)

Expand Down

0 comments on commit a9d59f9

Please sign in to comment.