diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 9520db08b..230b49ccc 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -139,23 +139,45 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) { return ls[0], err } +// checkResultFound search for string and convert to NoReleasesErr +func checkResultFound(err error, name string) ([]*rspb.Release, error) { + if strings.Contains(err.Error(), "not found") { + return nil, fmt.Errorf("%q %s", name, NoReleasesErr) + } + return nil, err +} + // DeployedAll returns all deployed releases with the provided name, or // returns ErrReleaseNotFound if not found. func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) { s.Log("getting deployed releases from %q history", name) - ls, err := s.Driver.Query(map[string]string{ "NAME": name, "OWNER": "TILLER", "STATUS": "DEPLOYED", }) + if err == nil { return ls, nil } - if strings.Contains(err.Error(), "not found") { - return nil, fmt.Errorf("%q %s", name, NoReleasesErr) + + return checkResultFound(err, name) +} + +// SupersededAll returns all superseded releases with the provided name or +// returns ErrReleaseNotFound if not found. +func (s *Storage) SupersededAll(name string) ([]*rspb.Release, error) { + s.Log("getting superseded revisions from %q history", name) + ls, err := s.Driver.Query(map[string]string{ + "NAME": name, + "OWNER": "TILLER", + "STATUS": "SUPERSEDED", + }) + if err == nil { + return ls, nil } - return nil, err + + return checkResultFound(err, name) } // History returns the revision history for the release with the provided name, or diff --git a/pkg/tiller/release_rollback.go b/pkg/tiller/release_rollback.go index 1f72c3256..9a7889c9f 100644 --- a/pkg/tiller/release_rollback.go +++ b/pkg/tiller/release_rollback.go @@ -78,7 +78,11 @@ func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (* previousVersion := req.Version if req.Version == 0 { - previousVersion = currentRelease.Version - 1 + p := s.lastSupersededRelease(req.Name) + if p < 0 || err != nil { + return nil, nil, errInvalidRevision + } + previousVersion = p } s.Log("rolling back %s (current: v%d, target: v%d)", req.Name, currentRelease.Version, previousVersion) diff --git a/pkg/tiller/release_rollback_test.go b/pkg/tiller/release_rollback_test.go index 6aa895a63..4534901f9 100644 --- a/pkg/tiller/release_rollback_test.go +++ b/pkg/tiller/release_rollback_test.go @@ -184,6 +184,54 @@ func TestRollbackWithReleaseVersion(t *testing.T) { } } +func TestRollbackLatestSuperseded(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + + rel := releaseStub() + rel.Version = 1 + rel.Info.Status.Code = release.Status_SUPERSEDED + rs.env.Releases.Create(rel) + v2 := upgradeReleaseVersion(rel) + v2.Version = 2 + v2.Info.Status.Code = release.Status_FAILED + rs.env.Releases.Create(v2) + v3 := upgradeReleaseVersion(v2) + v3.Info.Status.Code = release.Status_DEPLOYED + rs.env.Releases.Create(v3) + + req := &services.RollbackReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Version: 0, + } + + _, err := rs.RollbackRelease(c, req) + 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) + } + + lastRelease, _ := rs.env.Releases.Get(rel.Name, 4) + statusCode := lastRelease.Info.Status.Code + description := lastRelease.Info.Description + + if statusCode != release.Status_DEPLOYED { + t.Errorf("Expected a new release created on deployed state, got: %s", statusCode) + } + if description != "Rollback to 1" { + t.Errorf("Expected to rollback for release 1, got: %s", description) + } +} + func TestRollbackDeleted(t *testing.T) { c := helm.NewContext() rs := rsFixture() diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index cf7748f44..8f8a01402 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -230,6 +230,15 @@ func (s *ReleaseServer) createUniqName(m moniker.Namer) (string, error) { return "ERROR", errors.New("no available release name found") } +func (s *ReleaseServer) lastSupersededRelease(name string) int32 { + releases, err := s.env.Releases.SupersededAll(name) + if len(releases) == 0 || err != nil { + return -1 + } + relutil.Reverse(releases, relutil.SortByRevision) + return releases[0].Version +} + func (s *ReleaseServer) engine(ch *chart.Chart) environment.Engine { renderer := s.env.EngineYard.Default() if ch.Metadata.Engine != "" { diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index d94ea2eeb..db4a564ef 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -381,6 +381,40 @@ func TestUniqName(t *testing.T) { } } +func TestLastSupersededRelease(t *testing.T) { + rs := rsFixture() + + sr := rs.lastSupersededRelease("non-existent-name") + if sr != -1 { + t.Errorf("Expected -1 return for a not existent release.") + } + + rel1 := releaseStub() + rel1.Version = 1 + rel1.Info.Status.Code = release.Status_FAILED + rs.env.Releases.Create(rel1) + + rel2 := releaseStub() + rel2.Version = 2 + rel2.Info.Status.Code = release.Status_SUPERSEDED + rs.env.Releases.Create(rel2) + + rel3 := releaseStub() + rel3.Version = 3 + rel3.Info.Status.Code = release.Status_SUPERSEDED + rs.env.Releases.Create(rel3) + + rel4 := releaseStub() + rel4.Version = 4 + rel4.Info.Status.Code = release.Status_DEPLOYED + rs.env.Releases.Create(rel4) + + sr = rs.lastSupersededRelease("angry-panda") + if sr != 3 { + t.Errorf("Wrong release version return, got: %d", sr) + } +} + type fakeNamer struct { name string }