conditions.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. Copyright 2014 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 unversioned
  14. import (
  15. "fmt"
  16. "k8s.io/kubernetes/pkg/api"
  17. "k8s.io/kubernetes/pkg/api/errors"
  18. "k8s.io/kubernetes/pkg/api/unversioned"
  19. "k8s.io/kubernetes/pkg/apis/apps"
  20. "k8s.io/kubernetes/pkg/apis/batch"
  21. "k8s.io/kubernetes/pkg/apis/extensions"
  22. "k8s.io/kubernetes/pkg/util/wait"
  23. "k8s.io/kubernetes/pkg/watch"
  24. )
  25. // ControllerHasDesiredReplicas returns a condition that will be true if and only if
  26. // the desired replica count for a controller's ReplicaSelector equals the Replicas count.
  27. func ControllerHasDesiredReplicas(c Interface, controller *api.ReplicationController) wait.ConditionFunc {
  28. // If we're given a controller where the status lags the spec, it either means that the controller is stale,
  29. // or that the rc manager hasn't noticed the update yet. Polling status.Replicas is not safe in the latter case.
  30. desiredGeneration := controller.Generation
  31. return func() (bool, error) {
  32. ctrl, err := c.ReplicationControllers(controller.Namespace).Get(controller.Name)
  33. if err != nil {
  34. return false, err
  35. }
  36. // There's a chance a concurrent update modifies the Spec.Replicas causing this check to pass,
  37. // or, after this check has passed, a modification causes the rc manager to create more pods.
  38. // This will not be an issue once we've implemented graceful delete for rcs, but till then
  39. // concurrent stop operations on the same rc might have unintended side effects.
  40. return ctrl.Status.ObservedGeneration >= desiredGeneration && ctrl.Status.Replicas == ctrl.Spec.Replicas, nil
  41. }
  42. }
  43. // ReplicaSetHasDesiredReplicas returns a condition that will be true if and only if
  44. // the desired replica count for a ReplicaSet's ReplicaSelector equals the Replicas count.
  45. func ReplicaSetHasDesiredReplicas(c ExtensionsInterface, replicaSet *extensions.ReplicaSet) wait.ConditionFunc {
  46. // If we're given a ReplicaSet where the status lags the spec, it either means that the
  47. // ReplicaSet is stale, or that the ReplicaSet manager hasn't noticed the update yet.
  48. // Polling status.Replicas is not safe in the latter case.
  49. desiredGeneration := replicaSet.Generation
  50. return func() (bool, error) {
  51. rs, err := c.ReplicaSets(replicaSet.Namespace).Get(replicaSet.Name)
  52. if err != nil {
  53. return false, err
  54. }
  55. // There's a chance a concurrent update modifies the Spec.Replicas causing this check to
  56. // pass, or, after this check has passed, a modification causes the ReplicaSet manager to
  57. // create more pods. This will not be an issue once we've implemented graceful delete for
  58. // ReplicaSets, but till then concurrent stop operations on the same ReplicaSet might have
  59. // unintended side effects.
  60. return rs.Status.ObservedGeneration >= desiredGeneration && rs.Status.Replicas == rs.Spec.Replicas, nil
  61. }
  62. }
  63. func PetSetHasDesiredPets(c AppsInterface, petset *apps.PetSet) wait.ConditionFunc {
  64. // TODO: Differentiate between 0 pets and a really quick scale down using generation.
  65. return func() (bool, error) {
  66. ps, err := c.PetSets(petset.Namespace).Get(petset.Name)
  67. if err != nil {
  68. return false, err
  69. }
  70. return ps.Status.Replicas == ps.Spec.Replicas, nil
  71. }
  72. }
  73. // JobHasDesiredParallelism returns a condition that will be true if the desired parallelism count
  74. // for a job equals the current active counts or is less by an appropriate successful/unsuccessful count.
  75. func JobHasDesiredParallelism(c BatchInterface, job *batch.Job) wait.ConditionFunc {
  76. return func() (bool, error) {
  77. job, err := c.Jobs(job.Namespace).Get(job.Name)
  78. if err != nil {
  79. return false, err
  80. }
  81. // desired parallelism can be either the exact number, in which case return immediately
  82. if job.Status.Active == *job.Spec.Parallelism {
  83. return true, nil
  84. }
  85. if job.Spec.Completions == nil {
  86. // A job without specified completions needs to wait for Active to reach Parallelism.
  87. return false, nil
  88. } else {
  89. // otherwise count successful
  90. progress := *job.Spec.Completions - job.Status.Active - job.Status.Succeeded
  91. return progress == 0, nil
  92. }
  93. }
  94. }
  95. // DeploymentHasDesiredReplicas returns a condition that will be true if and only if
  96. // the desired replica count for a deployment equals its updated replicas count.
  97. // (non-terminated pods that have the desired template spec).
  98. func DeploymentHasDesiredReplicas(c ExtensionsInterface, deployment *extensions.Deployment) wait.ConditionFunc {
  99. // If we're given a deployment where the status lags the spec, it either
  100. // means that the deployment is stale, or that the deployment manager hasn't
  101. // noticed the update yet. Polling status.Replicas is not safe in the latter
  102. // case.
  103. desiredGeneration := deployment.Generation
  104. return func() (bool, error) {
  105. deployment, err := c.Deployments(deployment.Namespace).Get(deployment.Name)
  106. if err != nil {
  107. return false, err
  108. }
  109. return deployment.Status.ObservedGeneration >= desiredGeneration &&
  110. deployment.Status.UpdatedReplicas == deployment.Spec.Replicas, nil
  111. }
  112. }
  113. // ErrPodCompleted is returned by PodRunning or PodContainerRunning to indicate that
  114. // the pod has already reached completed state.
  115. var ErrPodCompleted = fmt.Errorf("pod ran to completion")
  116. // ErrContainerTerminated is returned by PodContainerRunning in the intermediate
  117. // state where the pod indicates it's still running, but its container is already terminated
  118. var ErrContainerTerminated = fmt.Errorf("container terminated")
  119. // PodRunning returns true if the pod is running, false if the pod has not yet reached running state,
  120. // returns ErrPodCompleted if the pod has run to completion, or an error in any other case.
  121. func PodRunning(event watch.Event) (bool, error) {
  122. switch event.Type {
  123. case watch.Deleted:
  124. return false, errors.NewNotFound(unversioned.GroupResource{Resource: "pods"}, "")
  125. }
  126. switch t := event.Object.(type) {
  127. case *api.Pod:
  128. switch t.Status.Phase {
  129. case api.PodRunning:
  130. return true, nil
  131. case api.PodFailed, api.PodSucceeded:
  132. return false, ErrPodCompleted
  133. }
  134. }
  135. return false, nil
  136. }
  137. // PodCompleted returns true if the pod has run to completion, false if the pod has not yet
  138. // reached running state, or an error in any other case.
  139. func PodCompleted(event watch.Event) (bool, error) {
  140. switch event.Type {
  141. case watch.Deleted:
  142. return false, errors.NewNotFound(unversioned.GroupResource{Resource: "pods"}, "")
  143. }
  144. switch t := event.Object.(type) {
  145. case *api.Pod:
  146. switch t.Status.Phase {
  147. case api.PodFailed, api.PodSucceeded:
  148. return true, nil
  149. }
  150. }
  151. return false, nil
  152. }
  153. // PodRunningAndReady returns true if the pod is running and ready, false if the pod has not
  154. // yet reached those states, returns ErrPodCompleted if the pod has run to completion, or
  155. // an error in any other case.
  156. func PodRunningAndReady(event watch.Event) (bool, error) {
  157. switch event.Type {
  158. case watch.Deleted:
  159. return false, errors.NewNotFound(unversioned.GroupResource{Resource: "pods"}, "")
  160. }
  161. switch t := event.Object.(type) {
  162. case *api.Pod:
  163. switch t.Status.Phase {
  164. case api.PodFailed, api.PodSucceeded:
  165. return false, ErrPodCompleted
  166. case api.PodRunning:
  167. return api.IsPodReady(t), nil
  168. }
  169. }
  170. return false, nil
  171. }
  172. // PodNotPending returns true if the pod has left the pending state, false if it has not,
  173. // or an error in any other case (such as if the pod was deleted).
  174. func PodNotPending(event watch.Event) (bool, error) {
  175. switch event.Type {
  176. case watch.Deleted:
  177. return false, errors.NewNotFound(unversioned.GroupResource{Resource: "pods"}, "")
  178. }
  179. switch t := event.Object.(type) {
  180. case *api.Pod:
  181. switch t.Status.Phase {
  182. case api.PodPending:
  183. return false, nil
  184. default:
  185. return true, nil
  186. }
  187. }
  188. return false, nil
  189. }
  190. // PodContainerRunning returns false until the named container has ContainerStatus running (at least once),
  191. // and will return an error if the pod is deleted, runs to completion, or the container pod is not available.
  192. func PodContainerRunning(containerName string) watch.ConditionFunc {
  193. return func(event watch.Event) (bool, error) {
  194. switch event.Type {
  195. case watch.Deleted:
  196. return false, errors.NewNotFound(unversioned.GroupResource{Resource: "pods"}, "")
  197. }
  198. switch t := event.Object.(type) {
  199. case *api.Pod:
  200. switch t.Status.Phase {
  201. case api.PodRunning, api.PodPending:
  202. case api.PodFailed, api.PodSucceeded:
  203. return false, ErrPodCompleted
  204. default:
  205. return false, nil
  206. }
  207. for _, s := range t.Status.ContainerStatuses {
  208. if s.Name != containerName {
  209. continue
  210. }
  211. if s.State.Terminated != nil {
  212. return false, ErrContainerTerminated
  213. }
  214. return s.State.Running != nil, nil
  215. }
  216. return false, nil
  217. }
  218. return false, nil
  219. }
  220. }
  221. // ServiceAccountHasSecrets returns true if the service account has at least one secret,
  222. // false if it does not, or an error.
  223. func ServiceAccountHasSecrets(event watch.Event) (bool, error) {
  224. switch event.Type {
  225. case watch.Deleted:
  226. return false, errors.NewNotFound(unversioned.GroupResource{Resource: "serviceaccounts"}, "")
  227. }
  228. switch t := event.Object.(type) {
  229. case *api.ServiceAccount:
  230. return len(t.Secrets) > 0, nil
  231. }
  232. return false, nil
  233. }