rollback.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package deployment
  14. import (
  15. "fmt"
  16. "reflect"
  17. "github.com/golang/glog"
  18. "k8s.io/kubernetes/pkg/api"
  19. "k8s.io/kubernetes/pkg/apis/extensions"
  20. deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
  21. )
  22. // Rolling back to a revision; no-op if the toRevision is deployment's current revision
  23. func (dc *DeploymentController) rollback(deployment *extensions.Deployment, toRevision *int64) (*extensions.Deployment, error) {
  24. newRS, allOldRSs, err := dc.getAllReplicaSetsAndSyncRevision(deployment, true)
  25. if err != nil {
  26. return nil, err
  27. }
  28. allRSs := append(allOldRSs, newRS)
  29. // If rollback revision is 0, rollback to the last revision
  30. if *toRevision == 0 {
  31. if *toRevision = deploymentutil.LastRevision(allRSs); *toRevision == 0 {
  32. // If we still can't find the last revision, gives up rollback
  33. dc.emitRollbackWarningEvent(deployment, deploymentutil.RollbackRevisionNotFound, "Unable to find last revision.")
  34. // Gives up rollback
  35. return dc.updateDeploymentAndClearRollbackTo(deployment)
  36. }
  37. }
  38. for _, rs := range allRSs {
  39. v, err := deploymentutil.Revision(rs)
  40. if err != nil {
  41. glog.V(4).Infof("Unable to extract revision from deployment's replica set %q: %v", rs.Name, err)
  42. continue
  43. }
  44. if v == *toRevision {
  45. glog.V(4).Infof("Found replica set %q with desired revision %d", rs.Name, v)
  46. // rollback by copying podTemplate.Spec from the replica set, and increment revision number by 1
  47. // no-op if the the spec matches current deployment's podTemplate.Spec
  48. deployment, performedRollback, err := dc.rollbackToTemplate(deployment, rs)
  49. if performedRollback && err == nil {
  50. dc.emitRollbackNormalEvent(deployment, fmt.Sprintf("Rolled back deployment %q to revision %d", deployment.Name, *toRevision))
  51. }
  52. return deployment, err
  53. }
  54. }
  55. dc.emitRollbackWarningEvent(deployment, deploymentutil.RollbackRevisionNotFound, "Unable to find the revision to rollback to.")
  56. // Gives up rollback
  57. return dc.updateDeploymentAndClearRollbackTo(deployment)
  58. }
  59. func (dc *DeploymentController) rollbackToTemplate(deployment *extensions.Deployment, rs *extensions.ReplicaSet) (d *extensions.Deployment, performedRollback bool, err error) {
  60. if !reflect.DeepEqual(deploymentutil.GetNewReplicaSetTemplate(deployment), rs.Spec.Template) {
  61. glog.Infof("Rolling back deployment %s to template spec %+v", deployment.Name, rs.Spec.Template.Spec)
  62. deploymentutil.SetFromReplicaSetTemplate(deployment, rs.Spec.Template)
  63. // set RS (the old RS we'll rolling back to) annotations back to the deployment;
  64. // otherwise, the deployment's current annotations (should be the same as current new RS) will be copied to the RS after the rollback.
  65. //
  66. // For example,
  67. // A Deployment has old RS1 with annotation {change-cause:create}, and new RS2 {change-cause:edit}.
  68. // Note that both annotations are copied from Deployment, and the Deployment should be annotated {change-cause:edit} as well.
  69. // Now, rollback Deployment to RS1, we should update Deployment's pod-template and also copy annotation from RS1.
  70. // Deployment is now annotated {change-cause:create}, and we have new RS1 {change-cause:create}, old RS2 {change-cause:edit}.
  71. //
  72. // If we don't copy the annotations back from RS to deployment on rollback, the Deployment will stay as {change-cause:edit},
  73. // and new RS1 becomes {change-cause:edit} (copied from deployment after rollback), old RS2 {change-cause:edit}, which is not correct.
  74. deploymentutil.SetDeploymentAnnotationsTo(deployment, rs)
  75. performedRollback = true
  76. } else {
  77. glog.V(4).Infof("Rolling back to a revision that contains the same template as current deployment %s, skipping rollback...", deployment.Name)
  78. dc.emitRollbackWarningEvent(deployment, deploymentutil.RollbackTemplateUnchanged, fmt.Sprintf("The rollback revision contains the same template as current deployment %q", deployment.Name))
  79. }
  80. d, err = dc.updateDeploymentAndClearRollbackTo(deployment)
  81. return
  82. }
  83. func (dc *DeploymentController) emitRollbackWarningEvent(deployment *extensions.Deployment, reason, message string) {
  84. dc.eventRecorder.Eventf(deployment, api.EventTypeWarning, reason, message)
  85. }
  86. func (dc *DeploymentController) emitRollbackNormalEvent(deployment *extensions.Deployment, message string) {
  87. dc.eventRecorder.Eventf(deployment, api.EventTypeNormal, deploymentutil.RollbackDone, message)
  88. }
  89. // updateDeploymentAndClearRollbackTo sets .spec.rollbackTo to nil and update the input deployment
  90. func (dc *DeploymentController) updateDeploymentAndClearRollbackTo(deployment *extensions.Deployment) (*extensions.Deployment, error) {
  91. glog.V(4).Infof("Cleans up rollbackTo of deployment %s", deployment.Name)
  92. deployment.Spec.RollbackTo = nil
  93. return dc.client.Extensions().Deployments(deployment.ObjectMeta.Namespace).Update(deployment)
  94. }