Merge pull request #5143 from cleac/master

Fixes #3338: Implement "atomic" upgrade/install
pull/5227/head
Taylor Thomas 6 years ago committed by GitHub
commit 240e539cec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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 {
} }
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 {
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 {
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) {
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",

@ -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 {
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 {
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 {
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 {
// 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 {
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 {
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) {
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]
### 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]
* [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

@ -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]
* [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

Loading…
Cancel
Save