mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-23 14:50:24 +00:00
118 lines
5.5 KiB
Go
118 lines
5.5 KiB
Go
/*
|
|
Copyright 2016 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package deployment
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/golang/glog"
|
|
|
|
"k8s.io/api/core/v1"
|
|
extensions "k8s.io/api/extensions/v1beta1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
|
)
|
|
|
|
// rollback the deployment to the specified revision. In any case cleanup the rollback spec.
|
|
func (dc *DeploymentController) rollback(d *extensions.Deployment, rsList []*extensions.ReplicaSet, podMap map[types.UID]*v1.PodList) error {
|
|
newRS, allOldRSs, err := dc.getAllReplicaSetsAndSyncRevision(d, rsList, podMap, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
allRSs := append(allOldRSs, newRS)
|
|
toRevision := &d.Spec.RollbackTo.Revision
|
|
// If rollback revision is 0, rollback to the last revision
|
|
if *toRevision == 0 {
|
|
if *toRevision = deploymentutil.LastRevision(allRSs); *toRevision == 0 {
|
|
// If we still can't find the last revision, gives up rollback
|
|
dc.emitRollbackWarningEvent(d, deploymentutil.RollbackRevisionNotFound, "Unable to find last revision.")
|
|
// Gives up rollback
|
|
return dc.updateDeploymentAndClearRollbackTo(d)
|
|
}
|
|
}
|
|
for _, rs := range allRSs {
|
|
v, err := deploymentutil.Revision(rs)
|
|
if err != nil {
|
|
glog.V(4).Infof("Unable to extract revision from deployment's replica set %q: %v", rs.Name, err)
|
|
continue
|
|
}
|
|
if v == *toRevision {
|
|
glog.V(4).Infof("Found replica set %q with desired revision %d", rs.Name, v)
|
|
// rollback by copying podTemplate.Spec from the replica set
|
|
// revision number will be incremented during the next getAllReplicaSetsAndSyncRevision call
|
|
// no-op if the spec matches current deployment's podTemplate.Spec
|
|
performedRollback, err := dc.rollbackToTemplate(d, rs)
|
|
if performedRollback && err == nil {
|
|
dc.emitRollbackNormalEvent(d, fmt.Sprintf("Rolled back deployment %q to revision %d", d.Name, *toRevision))
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
dc.emitRollbackWarningEvent(d, deploymentutil.RollbackRevisionNotFound, "Unable to find the revision to rollback to.")
|
|
// Gives up rollback
|
|
return dc.updateDeploymentAndClearRollbackTo(d)
|
|
}
|
|
|
|
// rollbackToTemplate compares the templates of the provided deployment and replica set and
|
|
// updates the deployment with the replica set template in case they are different. It also
|
|
// cleans up the rollback spec so subsequent requeues of the deployment won't end up in here.
|
|
func (dc *DeploymentController) rollbackToTemplate(d *extensions.Deployment, rs *extensions.ReplicaSet) (bool, error) {
|
|
performedRollback := false
|
|
if !deploymentutil.EqualIgnoreHash(&d.Spec.Template, &rs.Spec.Template) {
|
|
glog.V(4).Infof("Rolling back deployment %q to template spec %+v", d.Name, rs.Spec.Template.Spec)
|
|
deploymentutil.SetFromReplicaSetTemplate(d, rs.Spec.Template)
|
|
// set RS (the old RS we'll rolling back to) annotations back to the deployment;
|
|
// otherwise, the deployment's current annotations (should be the same as current new RS) will be copied to the RS after the rollback.
|
|
//
|
|
// For example,
|
|
// A Deployment has old RS1 with annotation {change-cause:create}, and new RS2 {change-cause:edit}.
|
|
// Note that both annotations are copied from Deployment, and the Deployment should be annotated {change-cause:edit} as well.
|
|
// Now, rollback Deployment to RS1, we should update Deployment's pod-template and also copy annotation from RS1.
|
|
// Deployment is now annotated {change-cause:create}, and we have new RS1 {change-cause:create}, old RS2 {change-cause:edit}.
|
|
//
|
|
// If we don't copy the annotations back from RS to deployment on rollback, the Deployment will stay as {change-cause:edit},
|
|
// and new RS1 becomes {change-cause:edit} (copied from deployment after rollback), old RS2 {change-cause:edit}, which is not correct.
|
|
deploymentutil.SetDeploymentAnnotationsTo(d, rs)
|
|
performedRollback = true
|
|
} else {
|
|
glog.V(4).Infof("Rolling back to a revision that contains the same template as current deployment %q, skipping rollback...", d.Name)
|
|
eventMsg := fmt.Sprintf("The rollback revision contains the same template as current deployment %q", d.Name)
|
|
dc.emitRollbackWarningEvent(d, deploymentutil.RollbackTemplateUnchanged, eventMsg)
|
|
}
|
|
|
|
return performedRollback, dc.updateDeploymentAndClearRollbackTo(d)
|
|
}
|
|
|
|
func (dc *DeploymentController) emitRollbackWarningEvent(d *extensions.Deployment, reason, message string) {
|
|
dc.eventRecorder.Eventf(d, v1.EventTypeWarning, reason, message)
|
|
}
|
|
|
|
func (dc *DeploymentController) emitRollbackNormalEvent(d *extensions.Deployment, message string) {
|
|
dc.eventRecorder.Eventf(d, v1.EventTypeNormal, deploymentutil.RollbackDone, message)
|
|
}
|
|
|
|
// updateDeploymentAndClearRollbackTo sets .spec.rollbackTo to nil and update the input deployment
|
|
// It is assumed that the caller will have updated the deployment template appropriately (in case
|
|
// we want to rollback).
|
|
func (dc *DeploymentController) updateDeploymentAndClearRollbackTo(d *extensions.Deployment) error {
|
|
glog.V(4).Infof("Cleans up rollbackTo of deployment %q", d.Name)
|
|
d.Spec.RollbackTo = nil
|
|
_, err := dc.client.ExtensionsV1beta1().Deployments(d.Namespace).Update(d)
|
|
return err
|
|
}
|