...

Source file src/k8s.io/kubernetes/pkg/controller/deployment/rollback.go

Documentation: k8s.io/kubernetes/pkg/controller/deployment

     1  /*
     2  Copyright 2016 The Kubernetes 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 deployment
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strconv"
    23  
    24  	apps "k8s.io/api/apps/v1"
    25  	v1 "k8s.io/api/core/v1"
    26  	extensions "k8s.io/api/extensions/v1beta1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/klog/v2"
    29  	deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
    30  )
    31  
    32  // rollback the deployment to the specified revision. In any case cleanup the rollback spec.
    33  func (dc *DeploymentController) rollback(ctx context.Context, d *apps.Deployment, rsList []*apps.ReplicaSet) error {
    34  	logger := klog.FromContext(ctx)
    35  	newRS, allOldRSs, err := dc.getAllReplicaSetsAndSyncRevision(ctx, d, rsList, true)
    36  	if err != nil {
    37  		return err
    38  	}
    39  
    40  	allRSs := append(allOldRSs, newRS)
    41  	rollbackTo := getRollbackTo(d)
    42  	// If rollback revision is 0, rollback to the last revision
    43  	if rollbackTo.Revision == 0 {
    44  		if rollbackTo.Revision = deploymentutil.LastRevision(logger, allRSs); rollbackTo.Revision == 0 {
    45  			// If we still can't find the last revision, gives up rollback
    46  			dc.emitRollbackWarningEvent(d, deploymentutil.RollbackRevisionNotFound, "Unable to find last revision.")
    47  			// Gives up rollback
    48  			return dc.updateDeploymentAndClearRollbackTo(ctx, d)
    49  		}
    50  	}
    51  	for _, rs := range allRSs {
    52  		v, err := deploymentutil.Revision(rs)
    53  		if err != nil {
    54  			logger.V(4).Info("Unable to extract revision from deployment's replica set", "replicaSet", klog.KObj(rs), "err", err)
    55  			continue
    56  		}
    57  		if v == rollbackTo.Revision {
    58  			logger.V(4).Info("Found replica set with desired revision", "replicaSet", klog.KObj(rs), "revision", v)
    59  			// rollback by copying podTemplate.Spec from the replica set
    60  			// revision number will be incremented during the next getAllReplicaSetsAndSyncRevision call
    61  			// no-op if the spec matches current deployment's podTemplate.Spec
    62  			performedRollback, err := dc.rollbackToTemplate(ctx, d, rs)
    63  			if performedRollback && err == nil {
    64  				dc.emitRollbackNormalEvent(d, fmt.Sprintf("Rolled back deployment %q to revision %d", d.Name, rollbackTo.Revision))
    65  			}
    66  			return err
    67  		}
    68  	}
    69  	dc.emitRollbackWarningEvent(d, deploymentutil.RollbackRevisionNotFound, "Unable to find the revision to rollback to.")
    70  	// Gives up rollback
    71  	return dc.updateDeploymentAndClearRollbackTo(ctx, d)
    72  }
    73  
    74  // rollbackToTemplate compares the templates of the provided deployment and replica set and
    75  // updates the deployment with the replica set template in case they are different. It also
    76  // cleans up the rollback spec so subsequent requeues of the deployment won't end up in here.
    77  func (dc *DeploymentController) rollbackToTemplate(ctx context.Context, d *apps.Deployment, rs *apps.ReplicaSet) (bool, error) {
    78  	logger := klog.FromContext(ctx)
    79  	performedRollback := false
    80  	if !deploymentutil.EqualIgnoreHash(&d.Spec.Template, &rs.Spec.Template) {
    81  		logger.V(4).Info("Rolling back deployment to old template spec", "deployment", klog.KObj(d), "templateSpec", rs.Spec.Template.Spec)
    82  		deploymentutil.SetFromReplicaSetTemplate(d, rs.Spec.Template)
    83  		// set RS (the old RS we'll rolling back to) annotations back to the deployment;
    84  		// otherwise, the deployment's current annotations (should be the same as current new RS) will be copied to the RS after the rollback.
    85  		//
    86  		// For example,
    87  		// A Deployment has old RS1 with annotation {change-cause:create}, and new RS2 {change-cause:edit}.
    88  		// Note that both annotations are copied from Deployment, and the Deployment should be annotated {change-cause:edit} as well.
    89  		// Now, rollback Deployment to RS1, we should update Deployment's pod-template and also copy annotation from RS1.
    90  		// Deployment is now annotated {change-cause:create}, and we have new RS1 {change-cause:create}, old RS2 {change-cause:edit}.
    91  		//
    92  		// If we don't copy the annotations back from RS to deployment on rollback, the Deployment will stay as {change-cause:edit},
    93  		// and new RS1 becomes {change-cause:edit} (copied from deployment after rollback), old RS2 {change-cause:edit}, which is not correct.
    94  		deploymentutil.SetDeploymentAnnotationsTo(d, rs)
    95  		performedRollback = true
    96  	} else {
    97  		logger.V(4).Info("Rolling back to a revision that contains the same template as current deployment, skipping rollback...", "deployment", klog.KObj(d))
    98  		eventMsg := fmt.Sprintf("The rollback revision contains the same template as current deployment %q", d.Name)
    99  		dc.emitRollbackWarningEvent(d, deploymentutil.RollbackTemplateUnchanged, eventMsg)
   100  	}
   101  
   102  	return performedRollback, dc.updateDeploymentAndClearRollbackTo(ctx, d)
   103  }
   104  
   105  func (dc *DeploymentController) emitRollbackWarningEvent(d *apps.Deployment, reason, message string) {
   106  	dc.eventRecorder.Eventf(d, v1.EventTypeWarning, reason, message)
   107  }
   108  
   109  func (dc *DeploymentController) emitRollbackNormalEvent(d *apps.Deployment, message string) {
   110  	dc.eventRecorder.Eventf(d, v1.EventTypeNormal, deploymentutil.RollbackDone, message)
   111  }
   112  
   113  // updateDeploymentAndClearRollbackTo sets .spec.rollbackTo to nil and update the input deployment
   114  // It is assumed that the caller will have updated the deployment template appropriately (in case
   115  // we want to rollback).
   116  func (dc *DeploymentController) updateDeploymentAndClearRollbackTo(ctx context.Context, d *apps.Deployment) error {
   117  	logger := klog.FromContext(ctx)
   118  	logger.V(4).Info("Cleans up rollbackTo of deployment", "deployment", klog.KObj(d))
   119  	setRollbackTo(d, nil)
   120  	_, err := dc.client.AppsV1().Deployments(d.Namespace).Update(ctx, d, metav1.UpdateOptions{})
   121  	return err
   122  }
   123  
   124  // TODO: Remove this when extensions/v1beta1 and apps/v1beta1 Deployment are dropped.
   125  func getRollbackTo(d *apps.Deployment) *extensions.RollbackConfig {
   126  	// Extract the annotation used for round-tripping the deprecated RollbackTo field.
   127  	revision := d.Annotations[apps.DeprecatedRollbackTo]
   128  	if revision == "" {
   129  		return nil
   130  	}
   131  	revision64, err := strconv.ParseInt(revision, 10, 64)
   132  	if err != nil {
   133  		// If it's invalid, ignore it.
   134  		return nil
   135  	}
   136  	return &extensions.RollbackConfig{
   137  		Revision: revision64,
   138  	}
   139  }
   140  
   141  // TODO: Remove this when extensions/v1beta1 and apps/v1beta1 Deployment are dropped.
   142  func setRollbackTo(d *apps.Deployment, rollbackTo *extensions.RollbackConfig) {
   143  	if rollbackTo == nil {
   144  		delete(d.Annotations, apps.DeprecatedRollbackTo)
   145  		return
   146  	}
   147  	if d.Annotations == nil {
   148  		d.Annotations = make(map[string]string)
   149  	}
   150  	d.Annotations[apps.DeprecatedRollbackTo] = strconv.FormatInt(rollbackTo.Revision, 10)
   151  }
   152  

View as plain text