fix(tiller): rollback deleted release (#3722) (#4820)

Solves #3722 by making the changes in #3539 more compatible with the previous behavior.

This gives a recovery option for "oops I deleted my helm release" by allowing rollback, which is intended to be a working feature of helm. Note that purging releases removes the history required to rollback, so this doesn't work in that case. However, purging takes significantly more time, so it's harder to accidentally purge everything.

Signed-off-by: Brent <bmperrea@gmail.com>
pull/4909/head
Brent 6 years ago committed by Matthew Fisher
parent 82d01cb312
commit 9ae00bebad

@ -25,6 +25,8 @@ import (
"k8s.io/helm/pkg/storage/driver"
)
const NoReleasesErr = "has no deployed releases"
// Storage represents a storage engine for a Release.
type Storage struct {
driver.Driver
@ -124,13 +126,13 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) {
ls, err := s.DeployedAll(name)
if err != nil {
if strings.Contains(err.Error(), "not found") {
return nil, fmt.Errorf("%q has no deployed releases", name)
return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
}
return nil, err
}
if len(ls) == 0 {
return nil, fmt.Errorf("%q has no deployed releases", name)
return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
}
return ls[0], err
@ -150,7 +152,7 @@ func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) {
return ls, nil
}
if strings.Contains(err.Error(), "not found") {
return nil, fmt.Errorf("%q has no deployed releases", name)
return nil, fmt.Errorf("%q %s", name, NoReleasesErr)
}
return nil, err
}

@ -18,6 +18,8 @@ package tiller
import (
"fmt"
"k8s.io/helm/pkg/storage"
"strings"
ctx "golang.org/x/net/context"
@ -151,11 +153,16 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R
}
}
// update the current release
s.Log("superseding previous deployment %d", currentRelease.Version)
currentRelease.Info.Status.Code = release.Status_SUPERSEDED
s.recordRelease(currentRelease, true)
// Supersede all previous deployments, see issue #2941.
deployed, err := s.env.Releases.DeployedAll(currentRelease.Name)
if err != nil {
if err != nil && !strings.Contains(err.Error(), storage.NoReleasesErr) {
return nil, err
}
// Supersede all previous deployments, see issue #2941.
for _, r := range deployed {
s.Log("superseding previous deployment %d", r.Version)
r.Info.Status.Code = release.Status_SUPERSEDED

@ -169,7 +169,7 @@ func TestRollbackWithReleaseVersion(t *testing.T) {
// check that v2 is now in a SUPERSEDED state
oldRel, err := rs.env.Releases.Get(rel.Name, 2)
if err != nil {
t.Fatalf("Failed to retrieve v1: %s", err)
t.Fatalf("Failed to retrieve v2: %s", err)
}
if oldRel.Info.Status.Code != release.Status_SUPERSEDED {
t.Errorf("Expected v2 to be in a SUPERSEDED state, got %q", oldRel.Info.Status.Code)
@ -184,6 +184,72 @@ func TestRollbackWithReleaseVersion(t *testing.T) {
}
}
func TestRollbackDeleted(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
rs.Log = t.Logf
rs.env.Releases.Log = t.Logf
rel2 := releaseStub()
rel2.Name = "other"
rs.env.Releases.Create(rel2)
rel := releaseStub()
rs.env.Releases.Create(rel)
v2 := upgradeReleaseVersion(rel)
rs.env.Releases.Update(rel)
rs.env.Releases.Create(v2)
v3 := upgradeReleaseVersion(v2)
// retain the original release as DEPLOYED while the update should fail
v2.Info.Status.Code = release.Status_DEPLOYED
v3.Info.Status.Code = release.Status_FAILED
rs.env.Releases.Update(v2)
rs.env.Releases.Create(v3)
req1 := &services.UninstallReleaseRequest{
Name: rel.Name,
DisableHooks: true,
}
_, err := rs.UninstallRelease(c, req1)
if err != nil {
t.Fatalf("Failed uninstall: %s", err)
}
oldRel, err := rs.env.Releases.Get(rel.Name, 3)
if err != nil {
t.Fatalf("Failed to retrieve v3: %s", err)
}
if oldRel.Info.Status.Code != release.Status_DELETED {
t.Errorf("Expected v3 to be in a DELETED state, got %q", oldRel.Info.Status.Code)
}
req2 := &services.RollbackReleaseRequest{
Name: rel.Name,
DisableHooks: true,
Version: 2,
}
_, err = rs.RollbackRelease(c, req2)
if err != nil {
t.Fatalf("Failed rollback: %s", err)
}
// check that v3 is now in a SUPERSEDED state
oldRel, err = rs.env.Releases.Get(rel.Name, 3)
if err != nil {
t.Fatalf("Failed to retrieve v3: %s", err)
}
if oldRel.Info.Status.Code != release.Status_SUPERSEDED {
t.Errorf("Expected v3 to be in a SUPERSEDED state, got %q", oldRel.Info.Status.Code)
}
// make sure we didn't update some other deployments.
otherRel, err := rs.env.Releases.Get(rel2.Name, 1)
if err != nil {
t.Fatalf("Failed to retrieve other v1: %s", err)
}
if otherRel.Info.Status.Code != release.Status_DEPLOYED {
t.Errorf("Expected other deployed release to stay untouched, got %q", otherRel.Info.Status.Code)
}
}
func TestRollbackReleaseNoHooks(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()

Loading…
Cancel
Save