fix(tiller): fix spurious "no release found" errors.

There are some places where releases are only located if they are in the
state DEPLOYED. That particular logic was incorrectly used for upgrades.
That caused #1566. While fixing that issue, I found that this was also
the root cause of #1587 (though because it was off by one). I added a
generic method to get the last release, regardless of its status.

This allows some behaviors that previously failed:

- 'helm upgrade' can now be performed on a DELETED release
- 'helm rollback' can now be performed on a DELETED release even if
  there is only one revision of that release history.

Closes #1566
Closes #1587
parent ec04da19
...@@ -139,6 +139,19 @@ func (s *Storage) History(name string) ([]*rspb.Release, error) { ...@@ -139,6 +139,19 @@ func (s *Storage) History(name string) ([]*rspb.Release, error) {
return l, nil return l, nil
} }
func (s *Storage) Last(name string) (*rspb.Release, error) {
h, err := s.History(name)
if err != nil {
return nil, err
}
if len(h) == 0 {
return nil, fmt.Errorf("no revision for release %q", name)
}
relutil.Reverse(h, relutil.SortByRevision)
return h[0], nil
}
// makeKey concatenates a release name and version into // makeKey concatenates a release name and version into
// a string with format ```<release_name>#v<version>```. // a string with format ```<release_name>#v<version>```.
// This key is used to uniquely identify storage objects. // This key is used to uniquely identify storage objects.
......
...@@ -217,6 +217,38 @@ func TestStorageHistory(t *testing.T) { ...@@ -217,6 +217,38 @@ func TestStorageHistory(t *testing.T) {
} }
} }
func TestStorageLast(t *testing.T) {
storage := Init(driver.NewMemory())
const name = "angry-bird"
// setup storage with test releases
setup := func() {
// release records
rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.Status_SUPERSEDED}.ToRelease()
rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.Status_FAILED}.ToRelease()
// create the release records in the storage
assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)")
assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)")
assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)")
assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)")
}
setup()
h, err := storage.Last(name)
if err != nil {
t.Fatalf("Failed to query for release history (%q): %s\n", name, err)
}
if h.Version != 4 {
t.Errorf("Expected revision 4, got %d", h.Version)
}
}
type ReleaseTestData struct { type ReleaseTestData struct {
Name string Name string
Version int32 Version int32
......
...@@ -229,17 +229,11 @@ func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease ...@@ -229,17 +229,11 @@ func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease
var rel *release.Release var rel *release.Release
if req.Version <= 0 { if req.Version <= 0 {
h, err := s.env.Releases.History(req.Name) var err error
rel, err = s.env.Releases.Last(req.Name)
if err != nil { if err != nil {
return nil, fmt.Errorf("getting deployed release '%s': %s", req.Name, err) return nil, fmt.Errorf("getting deployed release %q: %s", req.Name, err)
}
if len(h) < 1 {
return nil, errMissingRelease
} }
relutil.Reverse(h, relutil.SortByRevision)
rel = h[0]
} else { } else {
var err error var err error
if rel, err = s.env.Releases.Get(req.Name, req.Version); err != nil { if rel, err = s.env.Releases.Get(req.Name, req.Version); err != nil {
...@@ -376,7 +370,7 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele ...@@ -376,7 +370,7 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
} }
// finds the non-deleted release with the given name // finds the non-deleted release with the given name
currentRelease, err := s.env.Releases.Deployed(req.Name) currentRelease, err := s.env.Releases.Last(req.Name)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
...@@ -504,17 +498,10 @@ func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (* ...@@ -504,17 +498,10 @@ func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*
return nil, nil, errInvalidRevision return nil, nil, errInvalidRevision
} }
// finds the non-deleted release with the given name crls, err := s.env.Releases.Last(req.Name)
h, err := s.env.Releases.History(req.Name)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if len(h) <= 1 {
return nil, nil, errors.New("no revision to rollback")
}
relutil.SortByRevision(h)
crls := h[len(h)-1]
rbv := req.Version rbv := req.Version
if req.Version == 0 { if req.Version == 0 {
......
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