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 { ...@@ -131,6 +131,7 @@ type installCmd struct {
version string version string
timeout int64 timeout int64
wait bool wait bool
atomic bool
repoURL string repoURL string
username string username string
password string password string
...@@ -190,6 +191,8 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -190,6 +191,8 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
} }
inst.chartPath = cp inst.chartPath = cp
inst.client = ensureHelmClient(inst.client) inst.client = ensureHelmClient(inst.client)
inst.wait = inst.wait || inst.atomic
return inst.run() return inst.run()
}, },
} }
...@@ -212,6 +215,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { ...@@ -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.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.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.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.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.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") f.StringVar(&inst.password, "password", "", "chart repository password where to locate the requested chart")
...@@ -307,6 +311,23 @@ func (i *installCmd) run() error { ...@@ -307,6 +311,23 @@ func (i *installCmd) run() error {
helm.InstallWait(i.wait), helm.InstallWait(i.wait),
helm.InstallDescription(i.description)) helm.InstallDescription(i.description))
if err != nil { 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) return prettyError(err)
} }
......
...@@ -113,6 +113,14 @@ func TestInstall(t *testing.T) { ...@@ -113,6 +113,14 @@ func TestInstall(t *testing.T) {
expected: "apollo", expected: "apollo",
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "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 // Install, using the name-template
{ {
name: "install with name-template", name: "install with name-template",
......
...@@ -44,7 +44,7 @@ To customize the chart values, use any of ...@@ -44,7 +44,7 @@ To customize the chart values, use any of
- '--set-string' to provide key=val forcing val to be stored as a string, - '--set-string' to provide key=val forcing val to be stored as a string,
- '--set-file' to provide key=path to read a single large value from a file at path. - '--set-file' to provide key=path to read a single large value from a file at path.
To edit or append to the existing customized values, add the To edit or append to the existing customized values, add the
'--reuse-values' flag, otherwise any existing customized values are ignored. '--reuse-values' flag, otherwise any existing customized values are ignored.
If no chart value arguments are provided on the command line, any existing customized values are carried If no chart value arguments are provided on the command line, any existing customized values are carried
...@@ -105,6 +105,7 @@ type upgradeCmd struct { ...@@ -105,6 +105,7 @@ type upgradeCmd struct {
resetValues bool resetValues bool
reuseValues bool reuseValues bool
wait bool wait bool
atomic bool
repoURL string repoURL string
username string username string
password string password string
...@@ -142,6 +143,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { ...@@ -142,6 +143,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
upgrade.release = args[0] upgrade.release = args[0]
upgrade.chart = args[1] upgrade.chart = args[1]
upgrade.client = ensureHelmClient(upgrade.client) upgrade.client = ensureHelmClient(upgrade.client)
upgrade.wait = upgrade.wait || upgrade.atomic
return upgrade.run() return upgrade.run()
}, },
...@@ -167,6 +169,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { ...@@ -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.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.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.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.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.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") f.StringVar(&upgrade.password, "password", "", "chart repository password where to locate the requested chart")
...@@ -191,6 +194,8 @@ func (u *upgradeCmd) run() error { ...@@ -191,6 +194,8 @@ func (u *upgradeCmd) run() error {
return err return err
} }
releaseHistory, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1))
if u.install { if u.install {
// If a release does not exist, install it. If another error occurs during // If a release does not exist, install it. If another error occurs during
// the check, ignore the error and continue with the upgrade. // the check, ignore the error and continue with the upgrade.
...@@ -198,7 +203,6 @@ func (u *upgradeCmd) run() error { ...@@ -198,7 +203,6 @@ func (u *upgradeCmd) run() error {
// The returned error is a grpc.rpcError that wraps the message from the original 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 // So we're stuck doing string matching against the wrapped error, which is nested somewhere
// inside of the grpc.rpcError message. // inside of the grpc.rpcError message.
releaseHistory, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1))
if err == nil { if err == nil {
if u.namespace == "" { if u.namespace == "" {
...@@ -232,6 +236,7 @@ func (u *upgradeCmd) run() error { ...@@ -232,6 +236,7 @@ func (u *upgradeCmd) run() error {
timeout: u.timeout, timeout: u.timeout,
wait: u.wait, wait: u.wait,
description: u.description, description: u.description,
atomic: u.atomic,
} }
return ic.run() return ic.run()
} }
...@@ -270,6 +275,25 @@ func (u *upgradeCmd) run() error { ...@@ -270,6 +275,25 @@ func (u *upgradeCmd) run() error {
helm.UpgradeWait(u.wait), helm.UpgradeWait(u.wait),
helm.UpgradeDescription(u.description)) helm.UpgradeDescription(u.description))
if err != nil { 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)) return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err))
} }
......
...@@ -123,6 +123,14 @@ func TestUpgradeCmd(t *testing.T) { ...@@ -123,6 +123,14 @@ func TestUpgradeCmd(t *testing.T) {
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n", expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 5, Chart: ch2})}, 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'", name: "install a release with 'upgrade --install'",
args: []string{"zany-bunny", chartPath}, args: []string{"zany-bunny", chartPath},
......
...@@ -78,6 +78,7 @@ helm install [CHART] [flags] ...@@ -78,6 +78,7 @@ helm install [CHART] [flags]
### Options ### 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 --ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file --cert-file string identify HTTPS client using this SSL certificate file
--dep-up run helm dependency update before installing the chart --dep-up run helm dependency update before installing the chart
...@@ -129,4 +130,4 @@ helm install [CHART] [flags] ...@@ -129,4 +130,4 @@ helm install [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [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
...@@ -19,7 +19,7 @@ To customize the chart values, use any of ...@@ -19,7 +19,7 @@ To customize the chart values, use any of
- '--set-string' to provide key=val forcing val to be stored as a string, - '--set-string' to provide key=val forcing val to be stored as a string,
- '--set-file' to provide key=path to read a single large value from a file at path. - '--set-file' to provide key=path to read a single large value from a file at path.
To edit or append to the existing customized values, add the To edit or append to the existing customized values, add the
'--reuse-values' flag, otherwise any existing customized values are ignored. '--reuse-values' flag, otherwise any existing customized values are ignored.
If no chart value arguments are provided on the command line, any existing customized values are carried If no chart value arguments are provided on the command line, any existing customized values are carried
...@@ -65,6 +65,7 @@ helm upgrade [RELEASE] [CHART] [flags] ...@@ -65,6 +65,7 @@ helm upgrade [RELEASE] [CHART] [flags]
### Options ### 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 --ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file --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 --description string specify the description to use for the upgrade, rather than the default
...@@ -116,4 +117,4 @@ helm upgrade [RELEASE] [CHART] [flags] ...@@ -116,4 +117,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [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