feat: add "replace" option to upgrade cmd

Signed-off-by: Matt <matt@betmix.com>
pull/7668/head
Matt 6 years ago
parent 9f833e889e
commit d1bbb9630a

@ -216,6 +216,8 @@ message UpdateReleaseRequest {
bool subNotes = 13;
// Allow deletion of new resources created in this update when update failed
bool cleanup_on_fail = 14;
// If no deployed version of the release is available, replace an uninstalled, pending install, or failed release which remains in the history
bool replace = 15;
}
// UpdateReleaseResponse is the response to an update request.

@ -113,6 +113,7 @@ type upgradeCmd struct {
subNotes bool
description string
cleanupOnFail bool
replace bool
certFile string
keyFile string
@ -182,6 +183,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "Render subchart notes along with parent")
f.StringVar(&upgrade.description, "description", "", "Specify the description to use for the upgrade, rather than the default")
f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "Allow deletion of new resources created in this upgrade when upgrade failed")
f.BoolVar(&upgrade.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")
bindOutputFlag(cmd, &upgrade.output)
f.MarkDeprecated("disable-hooks", "Use --no-hooks instead")
@ -280,7 +282,9 @@ func (u *upgradeCmd) run() error {
helm.UpgradeSubNotes(u.subNotes),
helm.UpgradeWait(u.wait),
helm.UpgradeDescription(u.description),
helm.UpgradeCleanupOnFail(u.cleanupOnFail))
helm.UpgradeCleanupOnFail(u.cleanupOnFail),
helm.UpgradeReplace(u.replace),
)
if err != nil {
fmt.Fprintf(u.out, "UPGRADE FAILED\nError: %v\n", prettyError(err))
if u.atomic {

@ -83,6 +83,7 @@ helm upgrade [RELEASE] [CHART] [flags]
--password string Chart repository password where to locate the requested chart
--recreate-pods Performs pods restart for the resource if applicable
--render-subchart-notes Render subchart notes along with parent
--replace 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
--repo string Chart repository url where to locate the requested chart
--reset-values When upgrading, reset the values to the ones built into the chart
--reuse-values 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.
@ -119,4 +120,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 24-Sep-2019
###### Auto generated by spf13/cobra on 24-Feb-2020

@ -318,6 +318,13 @@ func UpgradeCleanupOnFail(cleanupOnFail bool) UpdateOption {
}
}
// If no deployed version of the release is available, replace an uninstalled, pending install, or failed release which remains in the history
func UpgradeReplace(replace bool) UpdateOption {
return func(opts *options) {
opts.updateReq.Replace = replace
}
}
// RollbackCleanupOnFail allows deletion of new resources created in this rollback when rollback failed
func RollbackCleanupOnFail(cleanupOnFail bool) RollbackOption {
return func(opts *options) {

@ -510,6 +510,8 @@ type UpdateReleaseRequest struct {
SubNotes bool `protobuf:"varint,13,opt,name=subNotes,proto3" json:"subNotes,omitempty"`
// Allow deletion of new resources created in this update when update failed
CleanupOnFail bool `protobuf:"varint,14,opt,name=cleanup_on_fail,json=cleanupOnFail,proto3" json:"cleanup_on_fail,omitempty"`
// If no deployed version of the release is available, replace an uninstalled, pending install, or failed release which remains in the history
Replace bool `protobuf:"varint,15,opt,name=replace,proto3" json:"replace,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`

@ -26,6 +26,7 @@ import (
"k8s.io/helm/pkg/hooks"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services"
"k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/timeconv"
)
@ -76,20 +77,36 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
return nil, nil, errMissingChart
}
// finds the deployed release with the given name
currentRelease, err := s.env.Releases.Deployed(req.Name)
// finds last the non-deleted release with the given name
lastRelease, err := s.env.Releases.Last(req.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, fmt.Errorf("%q %s", req.Name, storage.NoReleasesErr)
}
return nil, nil, err
}
status := lastRelease.Info.Status.Code
// determine if values will be reused
if err := s.reuseValues(req, currentRelease); err != nil {
var currentRelease *release.Release
if lastRelease.Info.Status.Code == release.Status_DEPLOYED {
// 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 = s.env.Releases.Deployed(req.Name)
if err != nil {
if req.Replace && strings.Contains(err.Error(), storage.NoReleasesErr) &&
(status == release.Status_FAILED || status == release.Status_PENDING_INSTALL || status == release.Status_DELETED) {
currentRelease = lastRelease
} else {
return nil, nil, err
}
}
}
// finds the non-deleted release with the given name
lastRelease, err := s.env.Releases.Last(req.Name)
if err != nil {
// determine if values will be reused
if err := s.reuseValues(req, currentRelease); err != nil {
return nil, nil, err
}

@ -611,6 +611,50 @@ func TestUpdateReleasePendingInstall_Force(t *testing.T) {
}
}
func TestUpdateReleaseFailure_Replace(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
rel := namedReleaseStub("replacing-luke", release.Status_FAILED)
rs.env.Releases.Create(rel)
rs.Log = t.Logf
req := &services.UpdateReleaseRequest{
Name: rel.Name,
DisableHooks: true,
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "templates/something", Data: []byte("text: 'Did you ever hear the tragedy of Darth Plagueis the Wise? I thought not. Its not a story the Jedi would tell you. Its a Sith legend. Darth Plagueis was a Dark Lord of the Sith, so powerful and so wise he could use the Force to influence the Midichlorians to create life... He had such a knowledge of the Dark Side that he could even keep the ones he cared about from dying. The Dark Side of the Force is a pathway to many abilities some consider to be unnatural. He became so powerful... The only thing he was afraid of was losing his power, which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew, then his apprentice killed him in his sleep. Ironic. He could save others from death, but not himself.'")},
},
},
Replace: true,
}
res, err := rs.UpdateRelease(c, req)
if err != nil {
t.Errorf("Expected successful update, got %v", err)
}
if updatedStatus := res.Release.Info.Status.Code; updatedStatus != release.Status_DEPLOYED {
t.Errorf("Expected DEPLOYED release. Got %d", updatedStatus)
}
compareStoredAndReturnedRelease(t, *rs, *res)
expectedDescription := "Upgrade complete"
if got := res.Release.Info.Description; got != expectedDescription {
t.Errorf("Expected description %q, got %q", expectedDescription, got)
}
oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version)
if err != nil {
t.Errorf("Expected to be able to get previous release")
}
if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED {
t.Errorf("Expected Superseded status on previous Release version. Got %v", oldStatus)
}
}
func compareStoredAndReturnedRelease(t *testing.T, rs ReleaseServer, res services.UpdateReleaseResponse) *release.Release {
storedRelease, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version)
if err != nil {

Loading…
Cancel
Save