deployment_util.go 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  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 util
  14. import (
  15. "context"
  16. "fmt"
  17. "math"
  18. "sort"
  19. "strconv"
  20. "strings"
  21. "time"
  22. apps "k8s.io/api/apps/v1"
  23. v1 "k8s.io/api/core/v1"
  24. apiequality "k8s.io/apimachinery/pkg/api/equality"
  25. "k8s.io/apimachinery/pkg/api/meta"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/labels"
  28. "k8s.io/apimachinery/pkg/runtime"
  29. "k8s.io/apimachinery/pkg/types"
  30. intstrutil "k8s.io/apimachinery/pkg/util/intstr"
  31. "k8s.io/apimachinery/pkg/util/wait"
  32. appsclient "k8s.io/client-go/kubernetes/typed/apps/v1"
  33. appslisters "k8s.io/client-go/listers/apps/v1"
  34. "k8s.io/klog"
  35. "k8s.io/kubernetes/pkg/controller"
  36. labelsutil "k8s.io/kubernetes/pkg/util/labels"
  37. "k8s.io/utils/integer"
  38. )
  39. const (
  40. // RevisionAnnotation is the revision annotation of a deployment's replica sets which records its rollout sequence
  41. RevisionAnnotation = "deployment.kubernetes.io/revision"
  42. // RevisionHistoryAnnotation maintains the history of all old revisions that a replica set has served for a deployment.
  43. RevisionHistoryAnnotation = "deployment.kubernetes.io/revision-history"
  44. // DesiredReplicasAnnotation is the desired replicas for a deployment recorded as an annotation
  45. // in its replica sets. Helps in separating scaling events from the rollout process and for
  46. // determining if the new replica set for a deployment is really saturated.
  47. DesiredReplicasAnnotation = "deployment.kubernetes.io/desired-replicas"
  48. // MaxReplicasAnnotation is the maximum replicas a deployment can have at a given point, which
  49. // is deployment.spec.replicas + maxSurge. Used by the underlying replica sets to estimate their
  50. // proportions in case the deployment has surge replicas.
  51. MaxReplicasAnnotation = "deployment.kubernetes.io/max-replicas"
  52. // RollbackRevisionNotFound is not found rollback event reason
  53. RollbackRevisionNotFound = "DeploymentRollbackRevisionNotFound"
  54. // RollbackTemplateUnchanged is the template unchanged rollback event reason
  55. RollbackTemplateUnchanged = "DeploymentRollbackTemplateUnchanged"
  56. // RollbackDone is the done rollback event reason
  57. RollbackDone = "DeploymentRollback"
  58. // Reasons for deployment conditions
  59. //
  60. // Progressing:
  61. // ReplicaSetUpdatedReason is added in a deployment when one of its replica sets is updated as part
  62. // of the rollout process.
  63. ReplicaSetUpdatedReason = "ReplicaSetUpdated"
  64. // FailedRSCreateReason is added in a deployment when it cannot create a new replica set.
  65. FailedRSCreateReason = "ReplicaSetCreateError"
  66. // NewReplicaSetReason is added in a deployment when it creates a new replica set.
  67. NewReplicaSetReason = "NewReplicaSetCreated"
  68. // FoundNewRSReason is added in a deployment when it adopts an existing replica set.
  69. FoundNewRSReason = "FoundNewReplicaSet"
  70. // NewRSAvailableReason is added in a deployment when its newest replica set is made available
  71. // ie. the number of new pods that have passed readiness checks and run for at least minReadySeconds
  72. // is at least the minimum available pods that need to run for the deployment.
  73. NewRSAvailableReason = "NewReplicaSetAvailable"
  74. // TimedOutReason is added in a deployment when its newest replica set fails to show any progress
  75. // within the given deadline (progressDeadlineSeconds).
  76. TimedOutReason = "ProgressDeadlineExceeded"
  77. // PausedDeployReason is added in a deployment when it is paused. Lack of progress shouldn't be
  78. // estimated once a deployment is paused.
  79. PausedDeployReason = "DeploymentPaused"
  80. // ResumedDeployReason is added in a deployment when it is resumed. Useful for not failing accidentally
  81. // deployments that paused amidst a rollout and are bounded by a deadline.
  82. ResumedDeployReason = "DeploymentResumed"
  83. //
  84. // Available:
  85. // MinimumReplicasAvailable is added in a deployment when it has its minimum replicas required available.
  86. MinimumReplicasAvailable = "MinimumReplicasAvailable"
  87. // MinimumReplicasUnavailable is added in a deployment when it doesn't have the minimum required replicas
  88. // available.
  89. MinimumReplicasUnavailable = "MinimumReplicasUnavailable"
  90. )
  91. // NewDeploymentCondition creates a new deployment condition.
  92. func NewDeploymentCondition(condType apps.DeploymentConditionType, status v1.ConditionStatus, reason, message string) *apps.DeploymentCondition {
  93. return &apps.DeploymentCondition{
  94. Type: condType,
  95. Status: status,
  96. LastUpdateTime: metav1.Now(),
  97. LastTransitionTime: metav1.Now(),
  98. Reason: reason,
  99. Message: message,
  100. }
  101. }
  102. // GetDeploymentCondition returns the condition with the provided type.
  103. func GetDeploymentCondition(status apps.DeploymentStatus, condType apps.DeploymentConditionType) *apps.DeploymentCondition {
  104. for i := range status.Conditions {
  105. c := status.Conditions[i]
  106. if c.Type == condType {
  107. return &c
  108. }
  109. }
  110. return nil
  111. }
  112. // SetDeploymentCondition updates the deployment to include the provided condition. If the condition that
  113. // we are about to add already exists and has the same status and reason then we are not going to update.
  114. func SetDeploymentCondition(status *apps.DeploymentStatus, condition apps.DeploymentCondition) {
  115. currentCond := GetDeploymentCondition(*status, condition.Type)
  116. if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason {
  117. return
  118. }
  119. // Do not update lastTransitionTime if the status of the condition doesn't change.
  120. if currentCond != nil && currentCond.Status == condition.Status {
  121. condition.LastTransitionTime = currentCond.LastTransitionTime
  122. }
  123. newConditions := filterOutCondition(status.Conditions, condition.Type)
  124. status.Conditions = append(newConditions, condition)
  125. }
  126. // RemoveDeploymentCondition removes the deployment condition with the provided type.
  127. func RemoveDeploymentCondition(status *apps.DeploymentStatus, condType apps.DeploymentConditionType) {
  128. status.Conditions = filterOutCondition(status.Conditions, condType)
  129. }
  130. // filterOutCondition returns a new slice of deployment conditions without conditions with the provided type.
  131. func filterOutCondition(conditions []apps.DeploymentCondition, condType apps.DeploymentConditionType) []apps.DeploymentCondition {
  132. var newConditions []apps.DeploymentCondition
  133. for _, c := range conditions {
  134. if c.Type == condType {
  135. continue
  136. }
  137. newConditions = append(newConditions, c)
  138. }
  139. return newConditions
  140. }
  141. // ReplicaSetToDeploymentCondition converts a replica set condition into a deployment condition.
  142. // Useful for promoting replica set failure conditions into deployments.
  143. func ReplicaSetToDeploymentCondition(cond apps.ReplicaSetCondition) apps.DeploymentCondition {
  144. return apps.DeploymentCondition{
  145. Type: apps.DeploymentConditionType(cond.Type),
  146. Status: cond.Status,
  147. LastTransitionTime: cond.LastTransitionTime,
  148. LastUpdateTime: cond.LastTransitionTime,
  149. Reason: cond.Reason,
  150. Message: cond.Message,
  151. }
  152. }
  153. // SetDeploymentRevision updates the revision for a deployment.
  154. func SetDeploymentRevision(deployment *apps.Deployment, revision string) bool {
  155. updated := false
  156. if deployment.Annotations == nil {
  157. deployment.Annotations = make(map[string]string)
  158. }
  159. if deployment.Annotations[RevisionAnnotation] != revision {
  160. deployment.Annotations[RevisionAnnotation] = revision
  161. updated = true
  162. }
  163. return updated
  164. }
  165. // MaxRevision finds the highest revision in the replica sets
  166. func MaxRevision(allRSs []*apps.ReplicaSet) int64 {
  167. max := int64(0)
  168. for _, rs := range allRSs {
  169. if v, err := Revision(rs); err != nil {
  170. // Skip the replica sets when it failed to parse their revision information
  171. klog.V(4).Infof("Error: %v. Couldn't parse revision for replica set %#v, deployment controller will skip it when reconciling revisions.", err, rs)
  172. } else if v > max {
  173. max = v
  174. }
  175. }
  176. return max
  177. }
  178. // LastRevision finds the second max revision number in all replica sets (the last revision)
  179. func LastRevision(allRSs []*apps.ReplicaSet) int64 {
  180. max, secMax := int64(0), int64(0)
  181. for _, rs := range allRSs {
  182. if v, err := Revision(rs); err != nil {
  183. // Skip the replica sets when it failed to parse their revision information
  184. klog.V(4).Infof("Error: %v. Couldn't parse revision for replica set %#v, deployment controller will skip it when reconciling revisions.", err, rs)
  185. } else if v >= max {
  186. secMax = max
  187. max = v
  188. } else if v > secMax {
  189. secMax = v
  190. }
  191. }
  192. return secMax
  193. }
  194. // Revision returns the revision number of the input object.
  195. func Revision(obj runtime.Object) (int64, error) {
  196. acc, err := meta.Accessor(obj)
  197. if err != nil {
  198. return 0, err
  199. }
  200. v, ok := acc.GetAnnotations()[RevisionAnnotation]
  201. if !ok {
  202. return 0, nil
  203. }
  204. return strconv.ParseInt(v, 10, 64)
  205. }
  206. // SetNewReplicaSetAnnotations sets new replica set's annotations appropriately by updating its revision and
  207. // copying required deployment annotations to it; it returns true if replica set's annotation is changed.
  208. func SetNewReplicaSetAnnotations(deployment *apps.Deployment, newRS *apps.ReplicaSet, newRevision string, exists bool, revHistoryLimitInChars int) bool {
  209. // First, copy deployment's annotations (except for apply and revision annotations)
  210. annotationChanged := copyDeploymentAnnotationsToReplicaSet(deployment, newRS)
  211. // Then, update replica set's revision annotation
  212. if newRS.Annotations == nil {
  213. newRS.Annotations = make(map[string]string)
  214. }
  215. oldRevision, ok := newRS.Annotations[RevisionAnnotation]
  216. // The newRS's revision should be the greatest among all RSes. Usually, its revision number is newRevision (the max revision number
  217. // of all old RSes + 1). However, it's possible that some of the old RSes are deleted after the newRS revision being updated, and
  218. // newRevision becomes smaller than newRS's revision. We should only update newRS revision when it's smaller than newRevision.
  219. oldRevisionInt, err := strconv.ParseInt(oldRevision, 10, 64)
  220. if err != nil {
  221. if oldRevision != "" {
  222. klog.Warningf("Updating replica set revision OldRevision not int %s", err)
  223. return false
  224. }
  225. //If the RS annotation is empty then initialise it to 0
  226. oldRevisionInt = 0
  227. }
  228. newRevisionInt, err := strconv.ParseInt(newRevision, 10, 64)
  229. if err != nil {
  230. klog.Warningf("Updating replica set revision NewRevision not int %s", err)
  231. return false
  232. }
  233. if oldRevisionInt < newRevisionInt {
  234. newRS.Annotations[RevisionAnnotation] = newRevision
  235. annotationChanged = true
  236. klog.V(4).Infof("Updating replica set %q revision to %s", newRS.Name, newRevision)
  237. }
  238. // If a revision annotation already existed and this replica set was updated with a new revision
  239. // then that means we are rolling back to this replica set. We need to preserve the old revisions
  240. // for historical information.
  241. if ok && oldRevisionInt < newRevisionInt {
  242. revisionHistoryAnnotation := newRS.Annotations[RevisionHistoryAnnotation]
  243. oldRevisions := strings.Split(revisionHistoryAnnotation, ",")
  244. if len(oldRevisions[0]) == 0 {
  245. newRS.Annotations[RevisionHistoryAnnotation] = oldRevision
  246. } else {
  247. totalLen := len(revisionHistoryAnnotation) + len(oldRevision) + 1
  248. // index for the starting position in oldRevisions
  249. start := 0
  250. for totalLen > revHistoryLimitInChars && start < len(oldRevisions) {
  251. totalLen = totalLen - len(oldRevisions[start]) - 1
  252. start++
  253. }
  254. if totalLen <= revHistoryLimitInChars {
  255. oldRevisions = append(oldRevisions[start:], oldRevision)
  256. newRS.Annotations[RevisionHistoryAnnotation] = strings.Join(oldRevisions, ",")
  257. } else {
  258. klog.Warningf("Not appending revision due to length limit of %v reached", revHistoryLimitInChars)
  259. }
  260. }
  261. }
  262. // If the new replica set is about to be created, we need to add replica annotations to it.
  263. if !exists && SetReplicasAnnotations(newRS, *(deployment.Spec.Replicas), *(deployment.Spec.Replicas)+MaxSurge(*deployment)) {
  264. annotationChanged = true
  265. }
  266. return annotationChanged
  267. }
  268. var annotationsToSkip = map[string]bool{
  269. v1.LastAppliedConfigAnnotation: true,
  270. RevisionAnnotation: true,
  271. RevisionHistoryAnnotation: true,
  272. DesiredReplicasAnnotation: true,
  273. MaxReplicasAnnotation: true,
  274. apps.DeprecatedRollbackTo: true,
  275. }
  276. // skipCopyAnnotation returns true if we should skip copying the annotation with the given annotation key
  277. // TODO: How to decide which annotations should / should not be copied?
  278. // See https://github.com/kubernetes/kubernetes/pull/20035#issuecomment-179558615
  279. func skipCopyAnnotation(key string) bool {
  280. return annotationsToSkip[key]
  281. }
  282. // copyDeploymentAnnotationsToReplicaSet copies deployment's annotations to replica set's annotations,
  283. // and returns true if replica set's annotation is changed.
  284. // Note that apply and revision annotations are not copied.
  285. func copyDeploymentAnnotationsToReplicaSet(deployment *apps.Deployment, rs *apps.ReplicaSet) bool {
  286. rsAnnotationsChanged := false
  287. if rs.Annotations == nil {
  288. rs.Annotations = make(map[string]string)
  289. }
  290. for k, v := range deployment.Annotations {
  291. // newRS revision is updated automatically in getNewReplicaSet, and the deployment's revision number is then updated
  292. // by copying its newRS revision number. We should not copy deployment's revision to its newRS, since the update of
  293. // deployment revision number may fail (revision becomes stale) and the revision number in newRS is more reliable.
  294. if skipCopyAnnotation(k) || rs.Annotations[k] == v {
  295. continue
  296. }
  297. rs.Annotations[k] = v
  298. rsAnnotationsChanged = true
  299. }
  300. return rsAnnotationsChanged
  301. }
  302. // SetDeploymentAnnotationsTo sets deployment's annotations as given RS's annotations.
  303. // This action should be done if and only if the deployment is rolling back to this rs.
  304. // Note that apply and revision annotations are not changed.
  305. func SetDeploymentAnnotationsTo(deployment *apps.Deployment, rollbackToRS *apps.ReplicaSet) {
  306. deployment.Annotations = getSkippedAnnotations(deployment.Annotations)
  307. for k, v := range rollbackToRS.Annotations {
  308. if !skipCopyAnnotation(k) {
  309. deployment.Annotations[k] = v
  310. }
  311. }
  312. }
  313. func getSkippedAnnotations(annotations map[string]string) map[string]string {
  314. skippedAnnotations := make(map[string]string)
  315. for k, v := range annotations {
  316. if skipCopyAnnotation(k) {
  317. skippedAnnotations[k] = v
  318. }
  319. }
  320. return skippedAnnotations
  321. }
  322. // FindActiveOrLatest returns the only active or the latest replica set in case there is at most one active
  323. // replica set. If there are more active replica sets, then we should proportionally scale them.
  324. func FindActiveOrLatest(newRS *apps.ReplicaSet, oldRSs []*apps.ReplicaSet) *apps.ReplicaSet {
  325. if newRS == nil && len(oldRSs) == 0 {
  326. return nil
  327. }
  328. sort.Sort(sort.Reverse(controller.ReplicaSetsByCreationTimestamp(oldRSs)))
  329. allRSs := controller.FilterActiveReplicaSets(append(oldRSs, newRS))
  330. switch len(allRSs) {
  331. case 0:
  332. // If there is no active replica set then we should return the newest.
  333. if newRS != nil {
  334. return newRS
  335. }
  336. return oldRSs[0]
  337. case 1:
  338. return allRSs[0]
  339. default:
  340. return nil
  341. }
  342. }
  343. // GetDesiredReplicasAnnotation returns the number of desired replicas
  344. func GetDesiredReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) {
  345. return getIntFromAnnotation(rs, DesiredReplicasAnnotation)
  346. }
  347. func getMaxReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) {
  348. return getIntFromAnnotation(rs, MaxReplicasAnnotation)
  349. }
  350. func getIntFromAnnotation(rs *apps.ReplicaSet, annotationKey string) (int32, bool) {
  351. annotationValue, ok := rs.Annotations[annotationKey]
  352. if !ok {
  353. return int32(0), false
  354. }
  355. intValue, err := strconv.Atoi(annotationValue)
  356. if err != nil {
  357. klog.V(2).Infof("Cannot convert the value %q with annotation key %q for the replica set %q", annotationValue, annotationKey, rs.Name)
  358. return int32(0), false
  359. }
  360. return int32(intValue), true
  361. }
  362. // SetReplicasAnnotations sets the desiredReplicas and maxReplicas into the annotations
  363. func SetReplicasAnnotations(rs *apps.ReplicaSet, desiredReplicas, maxReplicas int32) bool {
  364. updated := false
  365. if rs.Annotations == nil {
  366. rs.Annotations = make(map[string]string)
  367. }
  368. desiredString := fmt.Sprintf("%d", desiredReplicas)
  369. if hasString := rs.Annotations[DesiredReplicasAnnotation]; hasString != desiredString {
  370. rs.Annotations[DesiredReplicasAnnotation] = desiredString
  371. updated = true
  372. }
  373. maxString := fmt.Sprintf("%d", maxReplicas)
  374. if hasString := rs.Annotations[MaxReplicasAnnotation]; hasString != maxString {
  375. rs.Annotations[MaxReplicasAnnotation] = maxString
  376. updated = true
  377. }
  378. return updated
  379. }
  380. // ReplicasAnnotationsNeedUpdate return true if ReplicasAnnotations need to be updated
  381. func ReplicasAnnotationsNeedUpdate(rs *apps.ReplicaSet, desiredReplicas, maxReplicas int32) bool {
  382. if rs.Annotations == nil {
  383. return true
  384. }
  385. desiredString := fmt.Sprintf("%d", desiredReplicas)
  386. if hasString := rs.Annotations[DesiredReplicasAnnotation]; hasString != desiredString {
  387. return true
  388. }
  389. maxString := fmt.Sprintf("%d", maxReplicas)
  390. if hasString := rs.Annotations[MaxReplicasAnnotation]; hasString != maxString {
  391. return true
  392. }
  393. return false
  394. }
  395. // MaxUnavailable returns the maximum unavailable pods a rolling deployment can take.
  396. func MaxUnavailable(deployment apps.Deployment) int32 {
  397. if !IsRollingUpdate(&deployment) || *(deployment.Spec.Replicas) == 0 {
  398. return int32(0)
  399. }
  400. // Error caught by validation
  401. _, maxUnavailable, _ := ResolveFenceposts(deployment.Spec.Strategy.RollingUpdate.MaxSurge, deployment.Spec.Strategy.RollingUpdate.MaxUnavailable, *(deployment.Spec.Replicas))
  402. if maxUnavailable > *deployment.Spec.Replicas {
  403. return *deployment.Spec.Replicas
  404. }
  405. return maxUnavailable
  406. }
  407. // MinAvailable returns the minimum available pods of a given deployment
  408. func MinAvailable(deployment *apps.Deployment) int32 {
  409. if !IsRollingUpdate(deployment) {
  410. return int32(0)
  411. }
  412. return *(deployment.Spec.Replicas) - MaxUnavailable(*deployment)
  413. }
  414. // MaxSurge returns the maximum surge pods a rolling deployment can take.
  415. func MaxSurge(deployment apps.Deployment) int32 {
  416. if !IsRollingUpdate(&deployment) {
  417. return int32(0)
  418. }
  419. // Error caught by validation
  420. maxSurge, _, _ := ResolveFenceposts(deployment.Spec.Strategy.RollingUpdate.MaxSurge, deployment.Spec.Strategy.RollingUpdate.MaxUnavailable, *(deployment.Spec.Replicas))
  421. return maxSurge
  422. }
  423. // GetProportion will estimate the proportion for the provided replica set using 1. the current size
  424. // of the parent deployment, 2. the replica count that needs be added on the replica sets of the
  425. // deployment, and 3. the total replicas added in the replica sets of the deployment so far.
  426. func GetProportion(rs *apps.ReplicaSet, d apps.Deployment, deploymentReplicasToAdd, deploymentReplicasAdded int32) int32 {
  427. if rs == nil || *(rs.Spec.Replicas) == 0 || deploymentReplicasToAdd == 0 || deploymentReplicasToAdd == deploymentReplicasAdded {
  428. return int32(0)
  429. }
  430. rsFraction := getReplicaSetFraction(*rs, d)
  431. allowed := deploymentReplicasToAdd - deploymentReplicasAdded
  432. if deploymentReplicasToAdd > 0 {
  433. // Use the minimum between the replica set fraction and the maximum allowed replicas
  434. // when scaling up. This way we ensure we will not scale up more than the allowed
  435. // replicas we can add.
  436. return integer.Int32Min(rsFraction, allowed)
  437. }
  438. // Use the maximum between the replica set fraction and the maximum allowed replicas
  439. // when scaling down. This way we ensure we will not scale down more than the allowed
  440. // replicas we can remove.
  441. return integer.Int32Max(rsFraction, allowed)
  442. }
  443. // getReplicaSetFraction estimates the fraction of replicas a replica set can have in
  444. // 1. a scaling event during a rollout or 2. when scaling a paused deployment.
  445. func getReplicaSetFraction(rs apps.ReplicaSet, d apps.Deployment) int32 {
  446. // If we are scaling down to zero then the fraction of this replica set is its whole size (negative)
  447. if *(d.Spec.Replicas) == int32(0) {
  448. return -*(rs.Spec.Replicas)
  449. }
  450. deploymentReplicas := *(d.Spec.Replicas) + MaxSurge(d)
  451. annotatedReplicas, ok := getMaxReplicasAnnotation(&rs)
  452. if !ok {
  453. // If we cannot find the annotation then fallback to the current deployment size. Note that this
  454. // will not be an accurate proportion estimation in case other replica sets have different values
  455. // which means that the deployment was scaled at some point but we at least will stay in limits
  456. // due to the min-max comparisons in getProportion.
  457. annotatedReplicas = d.Status.Replicas
  458. }
  459. // We should never proportionally scale up from zero which means rs.spec.replicas and annotatedReplicas
  460. // will never be zero here.
  461. newRSsize := (float64(*(rs.Spec.Replicas) * deploymentReplicas)) / float64(annotatedReplicas)
  462. return integer.RoundToInt32(newRSsize) - *(rs.Spec.Replicas)
  463. }
  464. // GetAllReplicaSets returns the old and new replica sets targeted by the given Deployment. It gets PodList and ReplicaSetList from client interface.
  465. // Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets.
  466. // The third returned value is the new replica set, and it may be nil if it doesn't exist yet.
  467. func GetAllReplicaSets(deployment *apps.Deployment, c appsclient.AppsV1Interface) ([]*apps.ReplicaSet, []*apps.ReplicaSet, *apps.ReplicaSet, error) {
  468. rsList, err := ListReplicaSets(deployment, RsListFromClient(c))
  469. if err != nil {
  470. return nil, nil, nil, err
  471. }
  472. oldRSes, allOldRSes := FindOldReplicaSets(deployment, rsList)
  473. newRS := FindNewReplicaSet(deployment, rsList)
  474. return oldRSes, allOldRSes, newRS, nil
  475. }
  476. // GetOldReplicaSets returns the old replica sets targeted by the given Deployment; get PodList and ReplicaSetList from client interface.
  477. // Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets.
  478. func GetOldReplicaSets(deployment *apps.Deployment, c appsclient.AppsV1Interface) ([]*apps.ReplicaSet, []*apps.ReplicaSet, error) {
  479. rsList, err := ListReplicaSets(deployment, RsListFromClient(c))
  480. if err != nil {
  481. return nil, nil, err
  482. }
  483. oldRSes, allOldRSes := FindOldReplicaSets(deployment, rsList)
  484. return oldRSes, allOldRSes, nil
  485. }
  486. // GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface.
  487. // Returns nil if the new replica set doesn't exist yet.
  488. func GetNewReplicaSet(deployment *apps.Deployment, c appsclient.AppsV1Interface) (*apps.ReplicaSet, error) {
  489. rsList, err := ListReplicaSets(deployment, RsListFromClient(c))
  490. if err != nil {
  491. return nil, err
  492. }
  493. return FindNewReplicaSet(deployment, rsList), nil
  494. }
  495. // RsListFromClient returns an rsListFunc that wraps the given client.
  496. func RsListFromClient(c appsclient.AppsV1Interface) RsListFunc {
  497. return func(namespace string, options metav1.ListOptions) ([]*apps.ReplicaSet, error) {
  498. rsList, err := c.ReplicaSets(namespace).List(context.TODO(), options)
  499. if err != nil {
  500. return nil, err
  501. }
  502. var ret []*apps.ReplicaSet
  503. for i := range rsList.Items {
  504. ret = append(ret, &rsList.Items[i])
  505. }
  506. return ret, err
  507. }
  508. }
  509. // TODO: switch RsListFunc and podListFunc to full namespacers
  510. // RsListFunc returns the ReplicaSet from the ReplicaSet namespace and the List metav1.ListOptions.
  511. type RsListFunc func(string, metav1.ListOptions) ([]*apps.ReplicaSet, error)
  512. // podListFunc returns the PodList from the Pod namespace and the List metav1.ListOptions.
  513. type podListFunc func(string, metav1.ListOptions) (*v1.PodList, error)
  514. // ListReplicaSets returns a slice of RSes the given deployment targets.
  515. // Note that this does NOT attempt to reconcile ControllerRef (adopt/orphan),
  516. // because only the controller itself should do that.
  517. // However, it does filter out anything whose ControllerRef doesn't match.
  518. func ListReplicaSets(deployment *apps.Deployment, getRSList RsListFunc) ([]*apps.ReplicaSet, error) {
  519. // TODO: Right now we list replica sets by their labels. We should list them by selector, i.e. the replica set's selector
  520. // should be a superset of the deployment's selector, see https://github.com/kubernetes/kubernetes/issues/19830.
  521. namespace := deployment.Namespace
  522. selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
  523. if err != nil {
  524. return nil, err
  525. }
  526. options := metav1.ListOptions{LabelSelector: selector.String()}
  527. all, err := getRSList(namespace, options)
  528. if err != nil {
  529. return nil, err
  530. }
  531. // Only include those whose ControllerRef matches the Deployment.
  532. owned := make([]*apps.ReplicaSet, 0, len(all))
  533. for _, rs := range all {
  534. if metav1.IsControlledBy(rs, deployment) {
  535. owned = append(owned, rs)
  536. }
  537. }
  538. return owned, nil
  539. }
  540. // ListPods returns a list of pods the given deployment targets.
  541. // This needs a list of ReplicaSets for the Deployment,
  542. // which can be found with ListReplicaSets().
  543. // Note that this does NOT attempt to reconcile ControllerRef (adopt/orphan),
  544. // because only the controller itself should do that.
  545. // However, it does filter out anything whose ControllerRef doesn't match.
  546. func ListPods(deployment *apps.Deployment, rsList []*apps.ReplicaSet, getPodList podListFunc) (*v1.PodList, error) {
  547. namespace := deployment.Namespace
  548. selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
  549. if err != nil {
  550. return nil, err
  551. }
  552. options := metav1.ListOptions{LabelSelector: selector.String()}
  553. all, err := getPodList(namespace, options)
  554. if err != nil {
  555. return all, err
  556. }
  557. // Only include those whose ControllerRef points to a ReplicaSet that is in
  558. // turn owned by this Deployment.
  559. rsMap := make(map[types.UID]bool, len(rsList))
  560. for _, rs := range rsList {
  561. rsMap[rs.UID] = true
  562. }
  563. owned := &v1.PodList{Items: make([]v1.Pod, 0, len(all.Items))}
  564. for i := range all.Items {
  565. pod := &all.Items[i]
  566. controllerRef := metav1.GetControllerOf(pod)
  567. if controllerRef != nil && rsMap[controllerRef.UID] {
  568. owned.Items = append(owned.Items, *pod)
  569. }
  570. }
  571. return owned, nil
  572. }
  573. // EqualIgnoreHash returns true if two given podTemplateSpec are equal, ignoring the diff in value of Labels[pod-template-hash]
  574. // We ignore pod-template-hash because:
  575. // 1. The hash result would be different upon podTemplateSpec API changes
  576. // (e.g. the addition of a new field will cause the hash code to change)
  577. // 2. The deployment template won't have hash labels
  578. func EqualIgnoreHash(template1, template2 *v1.PodTemplateSpec) bool {
  579. t1Copy := template1.DeepCopy()
  580. t2Copy := template2.DeepCopy()
  581. // Remove hash labels from template.Labels before comparing
  582. delete(t1Copy.Labels, apps.DefaultDeploymentUniqueLabelKey)
  583. delete(t2Copy.Labels, apps.DefaultDeploymentUniqueLabelKey)
  584. return apiequality.Semantic.DeepEqual(t1Copy, t2Copy)
  585. }
  586. // FindNewReplicaSet returns the new RS this given deployment targets (the one with the same pod template).
  587. func FindNewReplicaSet(deployment *apps.Deployment, rsList []*apps.ReplicaSet) *apps.ReplicaSet {
  588. sort.Sort(controller.ReplicaSetsByCreationTimestamp(rsList))
  589. for i := range rsList {
  590. if EqualIgnoreHash(&rsList[i].Spec.Template, &deployment.Spec.Template) {
  591. // In rare cases, such as after cluster upgrades, Deployment may end up with
  592. // having more than one new ReplicaSets that have the same template as its template,
  593. // see https://github.com/kubernetes/kubernetes/issues/40415
  594. // We deterministically choose the oldest new ReplicaSet.
  595. return rsList[i]
  596. }
  597. }
  598. // new ReplicaSet does not exist.
  599. return nil
  600. }
  601. // FindOldReplicaSets returns the old replica sets targeted by the given Deployment, with the given slice of RSes.
  602. // Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets.
  603. func FindOldReplicaSets(deployment *apps.Deployment, rsList []*apps.ReplicaSet) ([]*apps.ReplicaSet, []*apps.ReplicaSet) {
  604. var requiredRSs []*apps.ReplicaSet
  605. var allRSs []*apps.ReplicaSet
  606. newRS := FindNewReplicaSet(deployment, rsList)
  607. for _, rs := range rsList {
  608. // Filter out new replica set
  609. if newRS != nil && rs.UID == newRS.UID {
  610. continue
  611. }
  612. allRSs = append(allRSs, rs)
  613. if *(rs.Spec.Replicas) != 0 {
  614. requiredRSs = append(requiredRSs, rs)
  615. }
  616. }
  617. return requiredRSs, allRSs
  618. }
  619. // SetFromReplicaSetTemplate sets the desired PodTemplateSpec from a replica set template to the given deployment.
  620. func SetFromReplicaSetTemplate(deployment *apps.Deployment, template v1.PodTemplateSpec) *apps.Deployment {
  621. deployment.Spec.Template.ObjectMeta = template.ObjectMeta
  622. deployment.Spec.Template.Spec = template.Spec
  623. deployment.Spec.Template.ObjectMeta.Labels = labelsutil.CloneAndRemoveLabel(
  624. deployment.Spec.Template.ObjectMeta.Labels,
  625. apps.DefaultDeploymentUniqueLabelKey)
  626. return deployment
  627. }
  628. // GetReplicaCountForReplicaSets returns the sum of Replicas of the given replica sets.
  629. func GetReplicaCountForReplicaSets(replicaSets []*apps.ReplicaSet) int32 {
  630. totalReplicas := int32(0)
  631. for _, rs := range replicaSets {
  632. if rs != nil {
  633. totalReplicas += *(rs.Spec.Replicas)
  634. }
  635. }
  636. return totalReplicas
  637. }
  638. // GetActualReplicaCountForReplicaSets returns the sum of actual replicas of the given replica sets.
  639. func GetActualReplicaCountForReplicaSets(replicaSets []*apps.ReplicaSet) int32 {
  640. totalActualReplicas := int32(0)
  641. for _, rs := range replicaSets {
  642. if rs != nil {
  643. totalActualReplicas += rs.Status.Replicas
  644. }
  645. }
  646. return totalActualReplicas
  647. }
  648. // GetReadyReplicaCountForReplicaSets returns the number of ready pods corresponding to the given replica sets.
  649. func GetReadyReplicaCountForReplicaSets(replicaSets []*apps.ReplicaSet) int32 {
  650. totalReadyReplicas := int32(0)
  651. for _, rs := range replicaSets {
  652. if rs != nil {
  653. totalReadyReplicas += rs.Status.ReadyReplicas
  654. }
  655. }
  656. return totalReadyReplicas
  657. }
  658. // GetAvailableReplicaCountForReplicaSets returns the number of available pods corresponding to the given replica sets.
  659. func GetAvailableReplicaCountForReplicaSets(replicaSets []*apps.ReplicaSet) int32 {
  660. totalAvailableReplicas := int32(0)
  661. for _, rs := range replicaSets {
  662. if rs != nil {
  663. totalAvailableReplicas += rs.Status.AvailableReplicas
  664. }
  665. }
  666. return totalAvailableReplicas
  667. }
  668. // IsRollingUpdate returns true if the strategy type is a rolling update.
  669. func IsRollingUpdate(deployment *apps.Deployment) bool {
  670. return deployment.Spec.Strategy.Type == apps.RollingUpdateDeploymentStrategyType
  671. }
  672. // DeploymentComplete considers a deployment to be complete once all of its desired replicas
  673. // are updated and available, and no old pods are running.
  674. func DeploymentComplete(deployment *apps.Deployment, newStatus *apps.DeploymentStatus) bool {
  675. return newStatus.UpdatedReplicas == *(deployment.Spec.Replicas) &&
  676. newStatus.Replicas == *(deployment.Spec.Replicas) &&
  677. newStatus.AvailableReplicas == *(deployment.Spec.Replicas) &&
  678. newStatus.ObservedGeneration >= deployment.Generation
  679. }
  680. // DeploymentProgressing reports progress for a deployment. Progress is estimated by comparing the
  681. // current with the new status of the deployment that the controller is observing. More specifically,
  682. // when new pods are scaled up or become ready or available, or old pods are scaled down, then we
  683. // consider the deployment is progressing.
  684. func DeploymentProgressing(deployment *apps.Deployment, newStatus *apps.DeploymentStatus) bool {
  685. oldStatus := deployment.Status
  686. // Old replicas that need to be scaled down
  687. oldStatusOldReplicas := oldStatus.Replicas - oldStatus.UpdatedReplicas
  688. newStatusOldReplicas := newStatus.Replicas - newStatus.UpdatedReplicas
  689. return (newStatus.UpdatedReplicas > oldStatus.UpdatedReplicas) ||
  690. (newStatusOldReplicas < oldStatusOldReplicas) ||
  691. newStatus.ReadyReplicas > deployment.Status.ReadyReplicas ||
  692. newStatus.AvailableReplicas > deployment.Status.AvailableReplicas
  693. }
  694. // used for unit testing
  695. var nowFn = func() time.Time { return time.Now() }
  696. // DeploymentTimedOut considers a deployment to have timed out once its condition that reports progress
  697. // is older than progressDeadlineSeconds or a Progressing condition with a TimedOutReason reason already
  698. // exists.
  699. func DeploymentTimedOut(deployment *apps.Deployment, newStatus *apps.DeploymentStatus) bool {
  700. if !HasProgressDeadline(deployment) {
  701. return false
  702. }
  703. // Look for the Progressing condition. If it doesn't exist, we have no base to estimate progress.
  704. // If it's already set with a TimedOutReason reason, we have already timed out, no need to check
  705. // again.
  706. condition := GetDeploymentCondition(*newStatus, apps.DeploymentProgressing)
  707. if condition == nil {
  708. return false
  709. }
  710. // If the previous condition has been a successful rollout then we shouldn't try to
  711. // estimate any progress. Scenario:
  712. //
  713. // * progressDeadlineSeconds is smaller than the difference between now and the time
  714. // the last rollout finished in the past.
  715. // * the creation of a new ReplicaSet triggers a resync of the Deployment prior to the
  716. // cached copy of the Deployment getting updated with the status.condition that indicates
  717. // the creation of the new ReplicaSet.
  718. //
  719. // The Deployment will be resynced and eventually its Progressing condition will catch
  720. // up with the state of the world.
  721. if condition.Reason == NewRSAvailableReason {
  722. return false
  723. }
  724. if condition.Reason == TimedOutReason {
  725. return true
  726. }
  727. // Look at the difference in seconds between now and the last time we reported any
  728. // progress or tried to create a replica set, or resumed a paused deployment and
  729. // compare against progressDeadlineSeconds.
  730. from := condition.LastUpdateTime
  731. now := nowFn()
  732. delta := time.Duration(*deployment.Spec.ProgressDeadlineSeconds) * time.Second
  733. timedOut := from.Add(delta).Before(now)
  734. klog.V(4).Infof("Deployment %q timed out (%t) [last progress check: %v - now: %v]", deployment.Name, timedOut, from, now)
  735. return timedOut
  736. }
  737. // NewRSNewReplicas calculates the number of replicas a deployment's new RS should have.
  738. // When one of the followings is true, we're rolling out the deployment; otherwise, we're scaling it.
  739. // 1) The new RS is saturated: newRS's replicas == deployment's replicas
  740. // 2) Max number of pods allowed is reached: deployment's replicas + maxSurge == all RSs' replicas
  741. func NewRSNewReplicas(deployment *apps.Deployment, allRSs []*apps.ReplicaSet, newRS *apps.ReplicaSet) (int32, error) {
  742. switch deployment.Spec.Strategy.Type {
  743. case apps.RollingUpdateDeploymentStrategyType:
  744. // Check if we can scale up.
  745. maxSurge, err := intstrutil.GetValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxSurge, int(*(deployment.Spec.Replicas)), true)
  746. if err != nil {
  747. return 0, err
  748. }
  749. // Find the total number of pods
  750. currentPodCount := GetReplicaCountForReplicaSets(allRSs)
  751. maxTotalPods := *(deployment.Spec.Replicas) + int32(maxSurge)
  752. if currentPodCount >= maxTotalPods {
  753. // Cannot scale up.
  754. return *(newRS.Spec.Replicas), nil
  755. }
  756. // Scale up.
  757. scaleUpCount := maxTotalPods - currentPodCount
  758. // Do not exceed the number of desired replicas.
  759. scaleUpCount = int32(integer.IntMin(int(scaleUpCount), int(*(deployment.Spec.Replicas)-*(newRS.Spec.Replicas))))
  760. return *(newRS.Spec.Replicas) + scaleUpCount, nil
  761. case apps.RecreateDeploymentStrategyType:
  762. return *(deployment.Spec.Replicas), nil
  763. default:
  764. return 0, fmt.Errorf("deployment type %v isn't supported", deployment.Spec.Strategy.Type)
  765. }
  766. }
  767. // IsSaturated checks if the new replica set is saturated by comparing its size with its deployment size.
  768. // Both the deployment and the replica set have to believe this replica set can own all of the desired
  769. // replicas in the deployment and the annotation helps in achieving that. All pods of the ReplicaSet
  770. // need to be available.
  771. func IsSaturated(deployment *apps.Deployment, rs *apps.ReplicaSet) bool {
  772. if rs == nil {
  773. return false
  774. }
  775. desiredString := rs.Annotations[DesiredReplicasAnnotation]
  776. desired, err := strconv.Atoi(desiredString)
  777. if err != nil {
  778. return false
  779. }
  780. return *(rs.Spec.Replicas) == *(deployment.Spec.Replicas) &&
  781. int32(desired) == *(deployment.Spec.Replicas) &&
  782. rs.Status.AvailableReplicas == *(deployment.Spec.Replicas)
  783. }
  784. // WaitForObservedDeployment polls for deployment to be updated so that deployment.Status.ObservedGeneration >= desiredGeneration.
  785. // Returns error if polling timesout.
  786. func WaitForObservedDeployment(getDeploymentFunc func() (*apps.Deployment, error), desiredGeneration int64, interval, timeout time.Duration) error {
  787. // TODO: This should take clientset.Interface when all code is updated to use clientset. Keeping it this way allows the function to be used by callers who have client.Interface.
  788. return wait.PollImmediate(interval, timeout, func() (bool, error) {
  789. deployment, err := getDeploymentFunc()
  790. if err != nil {
  791. return false, err
  792. }
  793. return deployment.Status.ObservedGeneration >= desiredGeneration, nil
  794. })
  795. }
  796. // ResolveFenceposts resolves both maxSurge and maxUnavailable. This needs to happen in one
  797. // step. For example:
  798. //
  799. // 2 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1), then old(-1), then new(+1)
  800. // 1 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1)
  801. // 2 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1)
  802. // 1 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1)
  803. // 2 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1)
  804. // 1 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1)
  805. func ResolveFenceposts(maxSurge, maxUnavailable *intstrutil.IntOrString, desired int32) (int32, int32, error) {
  806. surge, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt(0)), int(desired), true)
  807. if err != nil {
  808. return 0, 0, err
  809. }
  810. unavailable, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt(0)), int(desired), false)
  811. if err != nil {
  812. return 0, 0, err
  813. }
  814. if surge == 0 && unavailable == 0 {
  815. // Validation should never allow the user to explicitly use zero values for both maxSurge
  816. // maxUnavailable. Due to rounding down maxUnavailable though, it may resolve to zero.
  817. // If both fenceposts resolve to zero, then we should set maxUnavailable to 1 on the
  818. // theory that surge might not work due to quota.
  819. unavailable = 1
  820. }
  821. return int32(surge), int32(unavailable), nil
  822. }
  823. // HasProgressDeadline checks if the Deployment d is expected to surface the reason
  824. // "ProgressDeadlineExceeded" when the Deployment progress takes longer than expected time.
  825. func HasProgressDeadline(d *apps.Deployment) bool {
  826. return d.Spec.ProgressDeadlineSeconds != nil && *d.Spec.ProgressDeadlineSeconds != math.MaxInt32
  827. }
  828. // HasRevisionHistoryLimit checks if the Deployment d is expected to keep a specified number of
  829. // old replicaSets. These replicaSets are mainly kept with the purpose of rollback.
  830. // The RevisionHistoryLimit can start from 0 (no retained replicasSet). When set to math.MaxInt32,
  831. // the Deployment will keep all revisions.
  832. func HasRevisionHistoryLimit(d *apps.Deployment) bool {
  833. return d.Spec.RevisionHistoryLimit != nil && *d.Spec.RevisionHistoryLimit != math.MaxInt32
  834. }
  835. // GetDeploymentsForReplicaSet returns a list of Deployments that potentially
  836. // match a ReplicaSet. Only the one specified in the ReplicaSet's ControllerRef
  837. // will actually manage it.
  838. // Returns an error only if no matching Deployments are found.
  839. func GetDeploymentsForReplicaSet(deploymentLister appslisters.DeploymentLister, rs *apps.ReplicaSet) ([]*apps.Deployment, error) {
  840. if len(rs.Labels) == 0 {
  841. return nil, fmt.Errorf("no deployments found for ReplicaSet %v because it has no labels", rs.Name)
  842. }
  843. // TODO: MODIFY THIS METHOD so that it checks for the podTemplateSpecHash label
  844. dList, err := deploymentLister.Deployments(rs.Namespace).List(labels.Everything())
  845. if err != nil {
  846. return nil, err
  847. }
  848. var deployments []*apps.Deployment
  849. for _, d := range dList {
  850. selector, err := metav1.LabelSelectorAsSelector(d.Spec.Selector)
  851. if err != nil {
  852. return nil, fmt.Errorf("invalid label selector: %v", err)
  853. }
  854. // If a deployment with a nil or empty selector creeps in, it should match nothing, not everything.
  855. if selector.Empty() || !selector.Matches(labels.Set(rs.Labels)) {
  856. continue
  857. }
  858. deployments = append(deployments, d)
  859. }
  860. if len(deployments) == 0 {
  861. return nil, fmt.Errorf("could not find deployments set for ReplicaSet %s in namespace %s with labels: %v", rs.Name, rs.Namespace, rs.Labels)
  862. }
  863. return deployments, nil
  864. }