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

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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  
    22  	ctx "golang.org/x/net/context"
    23  
    24  	"k8s.io/helm/pkg/hooks"
    25  	"k8s.io/helm/pkg/proto/hapi/release"
    26  	"k8s.io/helm/pkg/proto/hapi/services"
    27  	"k8s.io/helm/pkg/timeconv"
    28  )
    29  
    30  // RollbackRelease rolls back to a previous version of the given release.
    31  func (s *ReleaseServer) RollbackRelease(c ctx.Context, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) {
    32  	err := s.env.Releases.LockRelease(req.Name)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	defer s.env.Releases.UnlockRelease(req.Name)
    37  
    38  	s.Log("preparing rollback of %s", req.Name)
    39  	currentRelease, targetRelease, err := s.prepareRollback(req)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	s.Log("performing rollback of %s", req.Name)
    45  	res, err := s.performRollback(currentRelease, targetRelease, req)
    46  	if err != nil {
    47  		return res, err
    48  	}
    49  
    50  	if !req.DryRun {
    51  		s.Log("creating rolled back release %s", req.Name)
    52  		if err := s.env.Releases.Create(targetRelease); err != nil {
    53  			return res, err
    54  		}
    55  	}
    56  
    57  	return res, nil
    58  }
    59  
    60  // prepareRollback finds the previous release and prepares a new release object with
    61  // the previous release's configuration
    62  func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*release.Release, *release.Release, error) {
    63  	switch {
    64  	case !ValidName.MatchString(req.Name):
    65  		return nil, nil, errMissingRelease
    66  	case req.Version < 0:
    67  		return nil, nil, errInvalidRevision
    68  	}
    69  
    70  	crls, err := s.env.Releases.Last(req.Name)
    71  	if err != nil {
    72  		return nil, nil, err
    73  	}
    74  
    75  	rbv := req.Version
    76  	if req.Version == 0 {
    77  		rbv = crls.Version - 1
    78  	}
    79  
    80  	s.Log("rolling back %s (current: v%d, target: v%d)", req.Name, crls.Version, rbv)
    81  
    82  	prls, err := s.env.Releases.Get(req.Name, rbv)
    83  	if err != nil {
    84  		return nil, nil, err
    85  	}
    86  
    87  	// Store a new release object with previous release's configuration
    88  	target := &release.Release{
    89  		Name:      req.Name,
    90  		Namespace: crls.Namespace,
    91  		Chart:     prls.Chart,
    92  		Config:    prls.Config,
    93  		Info: &release.Info{
    94  			FirstDeployed: crls.Info.FirstDeployed,
    95  			LastDeployed:  timeconv.Now(),
    96  			Status: &release.Status{
    97  				Code:  release.Status_UNKNOWN,
    98  				Notes: prls.Info.Status.Notes,
    99  			},
   100  			// Because we lose the reference to rbv elsewhere, we set the
   101  			// message here, and only override it later if we experience failure.
   102  			Description: fmt.Sprintf("Rollback to %d", rbv),
   103  		},
   104  		Version:     crls.Version + 1,
   105  		Manifest:    prls.Manifest,
   106  		Hooks:       prls.Hooks,
   107  		Annotations: prls.Annotations,
   108  	}
   109  
   110  	return crls, target, nil
   111  }
   112  
   113  func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) {
   114  	res := &services.RollbackReleaseResponse{Release: targetRelease}
   115  
   116  	if req.DryRun {
   117  		s.Log("dry run for %s", targetRelease.Name)
   118  		return res, nil
   119  	}
   120  
   121  	// pre-rollback hooks
   122  	if !req.DisableHooks {
   123  		if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PreRollback, req.Timeout); err != nil {
   124  			return res, err
   125  		}
   126  	} else {
   127  		s.Log("rollback hooks disabled for %s", req.Name)
   128  	}
   129  
   130  	if err := s.ReleaseModule.Rollback(currentRelease, targetRelease, req, s.env); err != nil {
   131  		msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err)
   132  		s.Log("warning: %s", msg)
   133  		currentRelease.Info.Status.Code = release.Status_SUPERSEDED
   134  		targetRelease.Info.Status.Code = release.Status_FAILED
   135  		targetRelease.Info.Description = msg
   136  		s.recordRelease(currentRelease, true)
   137  		s.recordRelease(targetRelease, false)
   138  		return res, err
   139  	}
   140  
   141  	// post-rollback hooks
   142  	if !req.DisableHooks {
   143  		if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PostRollback, req.Timeout); err != nil {
   144  			return res, err
   145  		}
   146  	}
   147  
   148  	currentRelease.Info.Status.Code = release.Status_SUPERSEDED
   149  	s.recordRelease(currentRelease, true)
   150  
   151  	targetRelease.Info.Status.Code = release.Status_DEPLOYED
   152  
   153  	return res, nil
   154  }