replicaset.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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 replicaset
  14. import (
  15. "fmt"
  16. "time"
  17. "github.com/golang/glog"
  18. "k8s.io/kubernetes/pkg/api"
  19. "k8s.io/kubernetes/pkg/api/errors"
  20. "k8s.io/kubernetes/pkg/api/unversioned"
  21. "k8s.io/kubernetes/pkg/apis/extensions"
  22. unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned"
  23. "k8s.io/kubernetes/pkg/labels"
  24. errorsutil "k8s.io/kubernetes/pkg/util/errors"
  25. labelsutil "k8s.io/kubernetes/pkg/util/labels"
  26. podutil "k8s.io/kubernetes/pkg/util/pod"
  27. "k8s.io/kubernetes/pkg/util/wait"
  28. )
  29. // TODO: use client library instead when it starts to support update retries
  30. // see https://github.com/kubernetes/kubernetes/issues/21479
  31. type updateRSFunc func(rs *extensions.ReplicaSet) error
  32. // UpdateRSWithRetries updates a RS with given applyUpdate function. Note that RS not found error is ignored.
  33. // The returned bool value can be used to tell if the RS is actually updated.
  34. func UpdateRSWithRetries(rsClient unversionedextensions.ReplicaSetInterface, rs *extensions.ReplicaSet, applyUpdate updateRSFunc) (*extensions.ReplicaSet, bool, error) {
  35. var err error
  36. var rsUpdated bool
  37. oldRs := rs
  38. if err = wait.Poll(10*time.Millisecond, 1*time.Minute, func() (bool, error) {
  39. rs, err = rsClient.Get(oldRs.Name)
  40. if err != nil {
  41. return false, err
  42. }
  43. // Apply the update, then attempt to push it to the apiserver.
  44. if err = applyUpdate(rs); err != nil {
  45. return false, err
  46. }
  47. if rs, err = rsClient.Update(rs); err == nil {
  48. // Update successful.
  49. return true, nil
  50. }
  51. // TODO: don't retry on perm-failed errors and handle them gracefully
  52. // Update could have failed due to conflict error. Try again.
  53. return false, nil
  54. }); err == nil {
  55. // When there's no error, we've updated this RS.
  56. rsUpdated = true
  57. }
  58. // Handle returned error from wait poll
  59. if err == wait.ErrWaitTimeout {
  60. err = fmt.Errorf("timed out trying to update RS: %#v", oldRs)
  61. }
  62. // Ignore the RS not found error, but the RS isn't updated.
  63. if errors.IsNotFound(err) {
  64. glog.V(4).Infof("%s %s/%s is not found, skip updating it.", oldRs.Kind, oldRs.Namespace, oldRs.Name)
  65. err = nil
  66. }
  67. // Ignore the precondition violated error, but the RS isn't updated.
  68. if err == errorsutil.ErrPreconditionViolated {
  69. glog.V(4).Infof("%s %s/%s precondition doesn't hold, skip updating it.", oldRs.Kind, oldRs.Namespace, oldRs.Name)
  70. err = nil
  71. }
  72. // If the error is non-nil the returned RS cannot be trusted; if rsUpdated is false, the contoller isn't updated;
  73. // if the error is nil and rsUpdated is true, the returned RS contains the applied update.
  74. return rs, rsUpdated, err
  75. }
  76. // GetPodTemplateSpecHash returns the pod template hash of a ReplicaSet's pod template space
  77. func GetPodTemplateSpecHash(rs extensions.ReplicaSet) string {
  78. meta := rs.Spec.Template.ObjectMeta
  79. meta.Labels = labelsutil.CloneAndRemoveLabel(meta.Labels, extensions.DefaultDeploymentUniqueLabelKey)
  80. return fmt.Sprintf("%d", podutil.GetPodTemplateSpecHash(api.PodTemplateSpec{
  81. ObjectMeta: meta,
  82. Spec: rs.Spec.Template.Spec,
  83. }))
  84. }
  85. // MatchingPodsFunc returns a filter function for pods with matching labels
  86. func MatchingPodsFunc(rs *extensions.ReplicaSet) (func(api.Pod) bool, error) {
  87. if rs == nil {
  88. return nil, nil
  89. }
  90. selector, err := unversioned.LabelSelectorAsSelector(rs.Spec.Selector)
  91. if err != nil {
  92. return nil, fmt.Errorf("invalid label selector: %v", err)
  93. }
  94. return func(pod api.Pod) bool {
  95. podLabelsSelector := labels.Set(pod.ObjectMeta.Labels)
  96. return selector.Matches(podLabelsSelector)
  97. }, nil
  98. }
  99. // ReplicaSetIsInactive returns a condition that will be true when a replica set is inactive ie.
  100. // it has zero running replicas.
  101. func ReplicaSetIsInactive(c unversionedextensions.ExtensionsInterface, replicaSet *extensions.ReplicaSet) wait.ConditionFunc {
  102. // If we're given a ReplicaSet where the status lags the spec, it either means that the
  103. // ReplicaSet is stale, or that the ReplicaSet manager hasn't noticed the update yet.
  104. // Polling status.Replicas is not safe in the latter case.
  105. desiredGeneration := replicaSet.Generation
  106. return func() (bool, error) {
  107. rs, err := c.ReplicaSets(replicaSet.Namespace).Get(replicaSet.Name)
  108. if err != nil {
  109. return false, err
  110. }
  111. return rs.Status.ObservedGeneration >= desiredGeneration &&
  112. rs.Spec.Replicas == 0 &&
  113. rs.Status.Replicas == 0 &&
  114. rs.Status.FullyLabeledReplicas == 0, nil
  115. }
  116. }