From 32590cde139faecc7e584a1dcde42635014defa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Bergstr=C3=B6m?= Date: Tue, 20 Feb 2018 15:48:49 +0100 Subject: [PATCH] fix(tiller): Supersede multiple deployments There are cases when multiple revisions of a release has been marked with DEPLOYED status. This makes sure any previous deployment will be set to SUPERSEDED when doing rollbacks. Closes #2941 #3513 #3275 --- pkg/storage/storage.go | 24 +++++++++++++++++++----- pkg/tiller/release_rollback.go | 12 ++++++++++-- pkg/tiller/release_server.go | 1 + 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 4ac5dbf4f..f76eb09f4 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -98,7 +98,7 @@ func (s *Storage) ListDeployed() ([]*rspb.Release, error) { }) } -// ListFilterAll returns the set of releases satisfying satisfying the predicate +// ListFilterAll returns the set of releases satisfying the predicate // (filter0 && filter1 && ... && filterN), i.e. a Release is included in the results // if and only if all filters return true. func (s *Storage) ListFilterAll(fns ...relutil.FilterFunc) ([]*rspb.Release, error) { @@ -108,7 +108,7 @@ func (s *Storage) ListFilterAll(fns ...relutil.FilterFunc) ([]*rspb.Release, err }) } -// ListFilterAny returns the set of releases satisfying satisfying the predicate +// ListFilterAny returns the set of releases satisfying the predicate // (filter0 || filter1 || ... || filterN), i.e. a Release is included in the results // if at least one of the filters returns true. func (s *Storage) ListFilterAny(fns ...relutil.FilterFunc) ([]*rspb.Release, error) { @@ -118,10 +118,24 @@ func (s *Storage) ListFilterAny(fns ...relutil.FilterFunc) ([]*rspb.Release, err }) } -// Deployed returns the deployed release with the provided release name, or +// Deployed returns the last deployed release with the provided release name, or // returns ErrReleaseNotFound if not found. func (s *Storage) Deployed(name string) (*rspb.Release, error) { - s.Log("getting deployed release from %q history", name) + 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, err + } + + return ls[0], 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, @@ -129,7 +143,7 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) { "STATUS": "DEPLOYED", }) if err == nil { - return ls[0], nil + return ls, nil } if strings.Contains(err.Error(), "not found") { return nil, fmt.Errorf("%q has no deployed releases", name) diff --git a/pkg/tiller/release_rollback.go b/pkg/tiller/release_rollback.go index 0c5cda946..fe9a6a032 100644 --- a/pkg/tiller/release_rollback.go +++ b/pkg/tiller/release_rollback.go @@ -146,8 +146,16 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R } } - currentRelease.Info.Status.Code = release.Status_SUPERSEDED - s.recordRelease(currentRelease, true) + deployed, err := s.env.Releases.DeployedAll(currentRelease.Name) + if err != nil { + 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 + s.recordRelease(r, true) + } targetRelease.Info.Status.Code = release.Status_DEPLOYED diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index 31a043030..a96c64938 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -316,6 +316,7 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values return hooks, b, notes, nil } +// recordRelease with an update operation in case reuse has been set. func (s *ReleaseServer) recordRelease(r *release.Release, reuse bool) { if reuse { if err := s.env.Releases.Update(r); err != nil {