Navigation Menu

Skip to content

Commit

Permalink
feat(helm): add '--replace' option to 'upgrade' command
Browse files Browse the repository at this point in the history
Fixes issue #5595

Signed-off-by: Matt Morrissette <yinzara@gmail.com>
  • Loading branch information
yinzara committed Feb 21, 2020
1 parent d6fad6b commit bfd7b96
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 9 deletions.
@@ -0,0 +1 @@
Error: UPGRADE FAILED: "funny-bunny" has no deployed releases
2 changes: 1 addition & 1 deletion cmd/helm/upgrade.go
Expand Up @@ -88,7 +88,6 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if err != nil {
return err
}

if client.Install {
// If a release does not exist, install it. If another error occurs during
// the check, ignore the error and continue with the upgrade.
Expand Down Expand Up @@ -176,6 +175,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
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.StringVar(&client.Description, "description", "", "add a custom description")
f.BoolVar(&client.Replace, "replace", false, "if no deployed version of the release is available, replace an uninstalled, pending install, or failed release which remains in the history. If no prior failed, uninstalled, pending install or deployed release is available, --replace will not install a new release unless --install is also specified. This is unsafe in production")
addChartPathOptionsFlags(f, &client.ChartPathOptions)
addValueOptionsFlags(f, valueOpts)
bindOutputFlag(cmd, &outfmt)
Expand Down
23 changes: 23 additions & 0 deletions cmd/helm/upgrade_test.go
Expand Up @@ -79,6 +79,10 @@ func TestUpgradeCmd(t *testing.T) {
missingDepsPath := "testdata/testcharts/chart-missing-deps"
badDepsPath := "testdata/testcharts/chart-bad-requirements"

relWithStatusMock := func(n string, v int, ch *chart.Chart, status release.Status) *release.Release {
return release.Mock(&release.MockReleaseOptions{Name: n, Version: v, Chart: ch, Status: status})
}

relMock := func(n string, v int, ch *chart.Chart) *release.Release {
return release.Mock(&release.MockReleaseOptions{Name: n, Version: v, Chart: ch})
}
Expand Down Expand Up @@ -138,6 +142,25 @@ func TestUpgradeCmd(t *testing.T) {
golden: "output/upgrade-with-bad-dependencies.txt",
wantError: true,
},
{
name: "upgrade a failed release",
cmd: fmt.Sprintf("upgrade funny-bunny '%s'", chartPath),
golden: "output/upgrade-with-bad-or-missing-existing-release.txt",
rels: []*release.Release{relWithStatusMock("funny-bunny", 1, ch, release.StatusFailed)},
wantError: true,
},
{
name: "upgrade a non-existant release using 'upgrade --replace'",
cmd: fmt.Sprintf("upgrade funny-bunny --replace '%s'", chartPath),
golden: "output/upgrade-with-bad-or-missing-existing-release.txt",
wantError: true,
},
{
name: "upgrade a failed release using 'upgrade --replace'",
cmd: fmt.Sprintf("upgrade funny-bunny --replace '%s'", chartPath),
golden: "output/upgrade.txt",
rels: []*release.Release{relWithStatusMock("funny-bunny", 2, ch, release.StatusFailed)},
},
}
runTestCmd(t, tests)
}
Expand Down
32 changes: 24 additions & 8 deletions pkg/action/upgrade.go
Expand Up @@ -62,6 +62,7 @@ type Upgrade struct {
Description string
PostRenderer postrender.PostRenderer
DisableOpenAPIValidation bool
Replace bool
}

// NewUpgrade creates a new Upgrade object with the given configuration.
Expand Down Expand Up @@ -122,12 +123,33 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
return nil, nil, errMissingChart
}

// finds the deployed release with the given name
currentRelease, err := u.cfg.Releases.Deployed(name)
// finds the last non-deleted release with the given name
lastRelease, err := u.cfg.Releases.Last(name)
if err != nil {
// to keep existing behavior of returning the "%q has no deployed releases" error when an existing release does not exist
if strings.Contains(err.Error(), "no revision for release") {
return nil, nil, errors.Errorf("%q has no deployed releases", name)
}
return nil, nil, err
}

var currentRelease *release.Release
if lastRelease.Info.Status == release.StatusDeployed {
// no need to retrieve the last deployed release from storage as the last release is deployed
currentRelease = lastRelease
} else {
// finds the deployed release with the given name
currentRelease, err = u.cfg.Releases.Deployed(name)
if err != nil {
if u.Replace && strings.Contains(err.Error(), "has no deployed releases") &&
(lastRelease.Info.Status == release.StatusFailed || lastRelease.Info.Status == release.StatusPendingInstall || lastRelease.Info.Status == release.StatusUninstalled) {
currentRelease = lastRelease
} else {
return nil, nil, err
}
}
}

// determine if values will be reused
vals, err = u.reuseValues(chart, currentRelease, vals)
if err != nil {
Expand All @@ -138,12 +160,6 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
return nil, nil, err
}

// finds the non-deleted release with the given name
lastRelease, err := u.cfg.Releases.Last(name)
if err != nil {
return nil, nil, err
}

// Increment revision count. This is passed to templates, and also stored on
// the release object.
revision := lastRelease.Version + 1
Expand Down

0 comments on commit bfd7b96

Please sign in to comment.