github.com/koderover/helm@v2.17.0+incompatible/pkg/tiller/release_rollback.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tiller
    18  
    19  import (
    20  	"fmt"
    21  	"k8s.io/helm/pkg/storage"
    22  	"strings"
    23  
    24  	ctx "golang.org/x/net/context"
    25  
    26  	"k8s.io/helm/pkg/hooks"
    27  	"k8s.io/helm/pkg/proto/hapi/release"
    28  	"k8s.io/helm/pkg/proto/hapi/services"
    29  	"k8s.io/helm/pkg/timeconv"
    30  )
    31  
    32  // RollbackRelease rolls back to a previous version of the given release.
    33  func (s *ReleaseServer) RollbackRelease(c ctx.Context, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) {
    34  	s.Log("preparing rollback of %s", req.Name)
    35  	currentRelease, targetRelease, err := s.prepareRollback(req)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	if !req.DryRun {
    41  		s.Log("creating rolled back release for %s", req.Name)
    42  		if err := s.env.Releases.Create(targetRelease); err != nil {
    43  			return nil, err
    44  		}
    45  	}
    46  	s.Log("performing rollback of %s", req.Name)
    47  	res, err := s.performRollback(currentRelease, targetRelease, req)
    48  	if err != nil {
    49  		return res, err
    50  	}
    51  
    52  	if !req.DryRun {
    53  		s.Log("updating status for rolled back release for %s", req.Name)
    54  		if err := s.env.Releases.Update(targetRelease); err != nil {
    55  			return res, err
    56  		}
    57  	}
    58  
    59  	return res, nil
    60  }
    61  
    62  // prepareRollback finds the previous release and prepares a new release object with
    63  // the previous release's configuration
    64  func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*release.Release, *release.Release, error) {
    65  	if err := validateReleaseName(req.Name); err != nil {
    66  		s.Log("prepareRollback: Release name is invalid: %s", req.Name)
    67  		return nil, nil, err
    68  	}
    69  
    70  	if req.Version < 0 {
    71  		return nil, nil, errInvalidRevision
    72  	}
    73  
    74  	currentRelease, err := s.env.Releases.Last(req.Name)
    75  	if err != nil {
    76  		return nil, nil, err
    77  	}
    78  
    79  	previousVersion := req.Version
    80  	if req.Version == 0 {
    81  		previousVersion = currentRelease.Version - 1
    82  	}
    83  
    84  	s.Log("rolling back %s (current: v%d, target: v%d)", req.Name, currentRelease.Version, previousVersion)
    85  
    86  	previousRelease, err := s.env.Releases.Get(req.Name, previousVersion)
    87  	if err != nil {
    88  		return nil, nil, err
    89  	}
    90  
    91  	description := req.Description
    92  	if req.Description == "" {
    93  		description = fmt.Sprintf("Rollback to %d", previousVersion)
    94  	}
    95  
    96  	// Store a new release object with previous release's configuration
    97  	targetRelease := &release.Release{
    98  		Name:      req.Name,
    99  		Namespace: currentRelease.Namespace,
   100  		Chart:     previousRelease.Chart,
   101  		Config:    previousRelease.Config,
   102  		Info: &release.Info{
   103  			FirstDeployed: currentRelease.Info.FirstDeployed,
   104  			LastDeployed:  timeconv.Now(),
   105  			Status: &release.Status{
   106  				Code:  release.Status_PENDING_ROLLBACK,
   107  				Notes: previousRelease.Info.Status.Notes,
   108  			},
   109  			// Because we lose the reference to previous version elsewhere, we set the
   110  			// message here, and only override it later if we experience failure.
   111  			Description: description,
   112  		},
   113  		Version:  currentRelease.Version + 1,
   114  		Manifest: previousRelease.Manifest,
   115  		Hooks:    previousRelease.Hooks,
   116  	}
   117  
   118  	return currentRelease, targetRelease, nil
   119  }
   120  
   121  func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) {
   122  	res := &services.RollbackReleaseResponse{Release: targetRelease}
   123  
   124  	if req.DryRun {
   125  		s.Log("dry run for %s", targetRelease.Name)
   126  		return res, nil
   127  	}
   128  
   129  	// pre-rollback hooks
   130  	if !req.DisableHooks {
   131  		if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PreRollback, req.Timeout); err != nil {
   132  			return res, err
   133  		}
   134  	} else {
   135  		s.Log("rollback hooks disabled for %s", req.Name)
   136  	}
   137  
   138  	if err := s.ReleaseModule.Rollback(currentRelease, targetRelease, req, s.env); err != nil {
   139  		msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err)
   140  		s.Log("warning: %s", msg)
   141  		currentRelease.Info.Status.Code = release.Status_SUPERSEDED
   142  		targetRelease.Info.Status.Code = release.Status_FAILED
   143  		targetRelease.Info.Description = msg
   144  		s.recordRelease(currentRelease, true)
   145  		s.recordRelease(targetRelease, true)
   146  		return res, err
   147  	}
   148  
   149  	// post-rollback hooks
   150  	if !req.DisableHooks {
   151  		if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PostRollback, req.Timeout); err != nil {
   152  			return res, err
   153  		}
   154  	}
   155  
   156  	// update the current release
   157  	s.Log("superseding previous deployment %d", currentRelease.Version)
   158  	currentRelease.Info.Status.Code = release.Status_SUPERSEDED
   159  	s.recordRelease(currentRelease, true)
   160  
   161  	// Supersede all previous deployments, see issue #2941.
   162  	deployed, err := s.env.Releases.DeployedAll(currentRelease.Name)
   163  	if err != nil && !strings.Contains(err.Error(), storage.NoReleasesErr) {
   164  		return nil, err
   165  	}
   166  	for _, r := range deployed {
   167  		s.Log("superseding previous deployment %d", r.Version)
   168  		r.Info.Status.Code = release.Status_SUPERSEDED
   169  		s.recordRelease(r, true)
   170  	}
   171  
   172  	targetRelease.Info.Status.Code = release.Status_DEPLOYED
   173  
   174  	return res, nil
   175  }