Unverified Commit 240e539c authored by Taylor Thomas's avatar Taylor Thomas Committed by GitHub

Merge pull request #5143 from cleac/master

Fixes #3338: Implement "atomic" upgrade/install
parents 0dfe2b86 2332b480
......@@ -131,6 +131,7 @@ type installCmd struct {
version string
timeout int64
wait bool
atomic bool
repoURL string
username string
password string
......@@ -190,6 +191,8 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
}
inst.chartPath = cp
inst.client = ensureHelmClient(inst.client)
inst.wait = inst.wait || inst.atomic
return inst.run()
},
}
......@@ -212,6 +215,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.StringVar(&inst.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed")
f.Int64Var(&inst.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&inst.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.BoolVar(&inst.atomic, "atomic", false, "if set, installation process purges chart on fail, also sets --wait flag")
f.StringVar(&inst.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&inst.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&inst.password, "password", "", "chart repository password where to locate the requested chart")
......@@ -307,6 +311,23 @@ func (i *installCmd) run() error {
helm.InstallWait(i.wait),
helm.InstallDescription(i.description))
if err != nil {
if i.atomic {
fmt.Fprintf(os.Stdout, "INSTALL FAILED\nPURGING CHART\nError: %v\n", prettyError(err))
deleteSideEffects := &deleteCmd{
name: i.name,
disableHooks: i.disableHooks,
purge: true,
timeout: i.timeout,
description: "",
dryRun: i.dryRun,
out: i.out,
client: i.client,
}
if err := deleteSideEffects.run(); err != nil {
return err
}
fmt.Fprintf(os.Stdout, "Successfully purged a chart!\n")
}
return prettyError(err)
}
......
......@@ -113,6 +113,14 @@ func TestInstall(t *testing.T) {
expected: "apollo",
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "apollo"}),
},
// Install, with atomic
{
name: "install with a atomic",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name apollo", " "),
expected: "apollo",
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "apollo"}),
},
// Install, using the name-template
{
name: "install with name-template",
......
......@@ -105,6 +105,7 @@ type upgradeCmd struct {
resetValues bool
reuseValues bool
wait bool
atomic bool
repoURL string
username string
password string
......@@ -142,6 +143,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
upgrade.release = args[0]
upgrade.chart = args[1]
upgrade.client = ensureHelmClient(upgrade.client)
upgrade.wait = upgrade.wait || upgrade.atomic
return upgrade.run()
},
......@@ -167,6 +169,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
f.BoolVar(&upgrade.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(&upgrade.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.BoolVar(&upgrade.atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag")
f.StringVar(&upgrade.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&upgrade.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&upgrade.password, "password", "", "chart repository password where to locate the requested chart")
......@@ -191,6 +194,8 @@ func (u *upgradeCmd) run() error {
return err
}
releaseHistory, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1))
if u.install {
// If a release does not exist, install it. If another error occurs during
// the check, ignore the error and continue with the upgrade.
......@@ -198,7 +203,6 @@ func (u *upgradeCmd) run() error {
// The returned error is a grpc.rpcError that wraps the message from the original error.
// So we're stuck doing string matching against the wrapped error, which is nested somewhere
// inside of the grpc.rpcError message.
releaseHistory, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1))
if err == nil {
if u.namespace == "" {
......@@ -232,6 +236,7 @@ func (u *upgradeCmd) run() error {
timeout: u.timeout,
wait: u.wait,
description: u.description,
atomic: u.atomic,
}
return ic.run()
}
......@@ -270,6 +275,25 @@ func (u *upgradeCmd) run() error {
helm.UpgradeWait(u.wait),
helm.UpgradeDescription(u.description))
if err != nil {
fmt.Fprintf(u.out, "UPGRADE FAILED\nROLLING BACK\nError: %v\n", prettyError(err))
if u.atomic {
rollback := &rollbackCmd{
out: u.out,
client: u.client,
name: u.release,
dryRun: u.dryRun,
recreate: u.recreate,
force: u.force,
timeout: u.timeout,
wait: u.wait,
description: "",
revision: releaseHistory.Releases[0].Version,
disableHooks: u.disableHooks,
}
if err := rollback.run(); err != nil {
return err
}
}
return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err))
}
......
......@@ -123,6 +123,14 @@ func TestUpgradeCmd(t *testing.T) {
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 5, Chart: ch2})},
},
{
name: "install a release with 'upgrade --atomic'",
args: []string{"funny-bunny", chartPath},
flags: []string{"--atomic"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 6, Chart: ch}),
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 6, Chart: ch})},
},
{
name: "install a release with 'upgrade --install'",
args: []string{"zany-bunny", chartPath},
......
......@@ -78,6 +78,7 @@ helm install [CHART] [flags]
### Options
```
--atomic if set, installation process purges chart on fail, also sets --wait flag
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file
--dep-up run helm dependency update before installing the chart
......@@ -129,4 +130,4 @@ helm install [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 1-Nov-2018
###### Auto generated by spf13/cobra on 28-Jan-2019
......@@ -65,6 +65,7 @@ helm upgrade [RELEASE] [CHART] [flags]
### Options
```
--atomic if set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file
--description string specify the description to use for the upgrade, rather than the default
......@@ -116,4 +117,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 1-Nov-2018
###### Auto generated by spf13/cobra on 28-Jan-2019
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment