util.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. Copyright 2015 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 pod
  14. import (
  15. "fmt"
  16. "time"
  17. v1 "k8s.io/api/core/v1"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/util/intstr"
  20. utilfeature "k8s.io/apiserver/pkg/util/feature"
  21. "k8s.io/kubernetes/pkg/features"
  22. )
  23. // FindPort locates the container port for the given pod and portName. If the
  24. // targetPort is a number, use that. If the targetPort is a string, look that
  25. // string up in all named ports in all containers in the target pod. If no
  26. // match is found, fail.
  27. func FindPort(pod *v1.Pod, svcPort *v1.ServicePort) (int, error) {
  28. portName := svcPort.TargetPort
  29. switch portName.Type {
  30. case intstr.String:
  31. name := portName.StrVal
  32. for _, container := range pod.Spec.Containers {
  33. for _, port := range container.Ports {
  34. if port.Name == name && port.Protocol == svcPort.Protocol {
  35. return int(port.ContainerPort), nil
  36. }
  37. }
  38. }
  39. case intstr.Int:
  40. return portName.IntValue(), nil
  41. }
  42. return 0, fmt.Errorf("no suitable port for manifest: %s", pod.UID)
  43. }
  44. // ContainerVisitor is called with each container spec, and returns true
  45. // if visiting should continue.
  46. type ContainerVisitor func(container *v1.Container) (shouldContinue bool)
  47. // VisitContainers invokes the visitor function with a pointer to the container
  48. // spec of every container in the given pod spec. If visitor returns false,
  49. // visiting is short-circuited. VisitContainers returns true if visiting completes,
  50. // false if visiting was short-circuited.
  51. func VisitContainers(podSpec *v1.PodSpec, visitor ContainerVisitor) bool {
  52. for i := range podSpec.InitContainers {
  53. if !visitor(&podSpec.InitContainers[i]) {
  54. return false
  55. }
  56. }
  57. for i := range podSpec.Containers {
  58. if !visitor(&podSpec.Containers[i]) {
  59. return false
  60. }
  61. }
  62. if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
  63. for i := range podSpec.EphemeralContainers {
  64. if !visitor((*v1.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon)) {
  65. return false
  66. }
  67. }
  68. }
  69. return true
  70. }
  71. // Visitor is called with each object name, and returns true if visiting should continue
  72. type Visitor func(name string) (shouldContinue bool)
  73. // VisitPodSecretNames invokes the visitor function with the name of every secret
  74. // referenced by the pod spec. If visitor returns false, visiting is short-circuited.
  75. // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
  76. // Returns true if visiting completed, false if visiting was short-circuited.
  77. func VisitPodSecretNames(pod *v1.Pod, visitor Visitor) bool {
  78. for _, reference := range pod.Spec.ImagePullSecrets {
  79. if !visitor(reference.Name) {
  80. return false
  81. }
  82. }
  83. VisitContainers(&pod.Spec, func(c *v1.Container) bool {
  84. return visitContainerSecretNames(c, visitor)
  85. })
  86. var source *v1.VolumeSource
  87. for i := range pod.Spec.Volumes {
  88. source = &pod.Spec.Volumes[i].VolumeSource
  89. switch {
  90. case source.AzureFile != nil:
  91. if len(source.AzureFile.SecretName) > 0 && !visitor(source.AzureFile.SecretName) {
  92. return false
  93. }
  94. case source.CephFS != nil:
  95. if source.CephFS.SecretRef != nil && !visitor(source.CephFS.SecretRef.Name) {
  96. return false
  97. }
  98. case source.Cinder != nil:
  99. if source.Cinder.SecretRef != nil && !visitor(source.Cinder.SecretRef.Name) {
  100. return false
  101. }
  102. case source.FlexVolume != nil:
  103. if source.FlexVolume.SecretRef != nil && !visitor(source.FlexVolume.SecretRef.Name) {
  104. return false
  105. }
  106. case source.Projected != nil:
  107. for j := range source.Projected.Sources {
  108. if source.Projected.Sources[j].Secret != nil {
  109. if !visitor(source.Projected.Sources[j].Secret.Name) {
  110. return false
  111. }
  112. }
  113. }
  114. case source.RBD != nil:
  115. if source.RBD.SecretRef != nil && !visitor(source.RBD.SecretRef.Name) {
  116. return false
  117. }
  118. case source.Secret != nil:
  119. if !visitor(source.Secret.SecretName) {
  120. return false
  121. }
  122. case source.ScaleIO != nil:
  123. if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) {
  124. return false
  125. }
  126. case source.ISCSI != nil:
  127. if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) {
  128. return false
  129. }
  130. case source.StorageOS != nil:
  131. if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) {
  132. return false
  133. }
  134. case source.CSI != nil:
  135. if source.CSI.NodePublishSecretRef != nil && !visitor(source.CSI.NodePublishSecretRef.Name) {
  136. return false
  137. }
  138. }
  139. }
  140. return true
  141. }
  142. func visitContainerSecretNames(container *v1.Container, visitor Visitor) bool {
  143. for _, env := range container.EnvFrom {
  144. if env.SecretRef != nil {
  145. if !visitor(env.SecretRef.Name) {
  146. return false
  147. }
  148. }
  149. }
  150. for _, envVar := range container.Env {
  151. if envVar.ValueFrom != nil && envVar.ValueFrom.SecretKeyRef != nil {
  152. if !visitor(envVar.ValueFrom.SecretKeyRef.Name) {
  153. return false
  154. }
  155. }
  156. }
  157. return true
  158. }
  159. // VisitPodConfigmapNames invokes the visitor function with the name of every configmap
  160. // referenced by the pod spec. If visitor returns false, visiting is short-circuited.
  161. // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited.
  162. // Returns true if visiting completed, false if visiting was short-circuited.
  163. func VisitPodConfigmapNames(pod *v1.Pod, visitor Visitor) bool {
  164. VisitContainers(&pod.Spec, func(c *v1.Container) bool {
  165. return visitContainerConfigmapNames(c, visitor)
  166. })
  167. var source *v1.VolumeSource
  168. for i := range pod.Spec.Volumes {
  169. source = &pod.Spec.Volumes[i].VolumeSource
  170. switch {
  171. case source.Projected != nil:
  172. for j := range source.Projected.Sources {
  173. if source.Projected.Sources[j].ConfigMap != nil {
  174. if !visitor(source.Projected.Sources[j].ConfigMap.Name) {
  175. return false
  176. }
  177. }
  178. }
  179. case source.ConfigMap != nil:
  180. if !visitor(source.ConfigMap.Name) {
  181. return false
  182. }
  183. }
  184. }
  185. return true
  186. }
  187. func visitContainerConfigmapNames(container *v1.Container, visitor Visitor) bool {
  188. for _, env := range container.EnvFrom {
  189. if env.ConfigMapRef != nil {
  190. if !visitor(env.ConfigMapRef.Name) {
  191. return false
  192. }
  193. }
  194. }
  195. for _, envVar := range container.Env {
  196. if envVar.ValueFrom != nil && envVar.ValueFrom.ConfigMapKeyRef != nil {
  197. if !visitor(envVar.ValueFrom.ConfigMapKeyRef.Name) {
  198. return false
  199. }
  200. }
  201. }
  202. return true
  203. }
  204. // GetContainerStatus extracts the status of container "name" from "statuses".
  205. // It also returns if "name" exists.
  206. func GetContainerStatus(statuses []v1.ContainerStatus, name string) (v1.ContainerStatus, bool) {
  207. for i := range statuses {
  208. if statuses[i].Name == name {
  209. return statuses[i], true
  210. }
  211. }
  212. return v1.ContainerStatus{}, false
  213. }
  214. // GetExistingContainerStatus extracts the status of container "name" from "statuses",
  215. // It also returns if "name" exists.
  216. func GetExistingContainerStatus(statuses []v1.ContainerStatus, name string) v1.ContainerStatus {
  217. status, _ := GetContainerStatus(statuses, name)
  218. return status
  219. }
  220. // IsPodAvailable returns true if a pod is available; false otherwise.
  221. // Precondition for an available pod is that it must be ready. On top
  222. // of that, there are two cases when a pod can be considered available:
  223. // 1. minReadySeconds == 0, or
  224. // 2. LastTransitionTime (is set) + minReadySeconds < current time
  225. func IsPodAvailable(pod *v1.Pod, minReadySeconds int32, now metav1.Time) bool {
  226. if !IsPodReady(pod) {
  227. return false
  228. }
  229. c := GetPodReadyCondition(pod.Status)
  230. minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
  231. if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
  232. return true
  233. }
  234. return false
  235. }
  236. // IsPodReady returns true if a pod is ready; false otherwise.
  237. func IsPodReady(pod *v1.Pod) bool {
  238. return IsPodReadyConditionTrue(pod.Status)
  239. }
  240. // IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
  241. func IsPodReadyConditionTrue(status v1.PodStatus) bool {
  242. condition := GetPodReadyCondition(status)
  243. return condition != nil && condition.Status == v1.ConditionTrue
  244. }
  245. // GetPodReadyCondition extracts the pod ready condition from the given status and returns that.
  246. // Returns nil if the condition is not present.
  247. func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition {
  248. _, condition := GetPodCondition(&status, v1.PodReady)
  249. return condition
  250. }
  251. // GetPodCondition extracts the provided condition from the given status and returns that.
  252. // Returns nil and -1 if the condition is not present, and the index of the located condition.
  253. func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
  254. if status == nil {
  255. return -1, nil
  256. }
  257. return GetPodConditionFromList(status.Conditions, conditionType)
  258. }
  259. // GetPodConditionFromList extracts the provided condition from the given list of condition and
  260. // returns the index of the condition and the condition. Returns -1 and nil if the condition is not present.
  261. func GetPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
  262. if conditions == nil {
  263. return -1, nil
  264. }
  265. for i := range conditions {
  266. if conditions[i].Type == conditionType {
  267. return i, &conditions[i]
  268. }
  269. }
  270. return -1, nil
  271. }
  272. // UpdatePodCondition updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the
  273. // status has changed.
  274. // Returns true if pod condition has changed or has been added.
  275. func UpdatePodCondition(status *v1.PodStatus, condition *v1.PodCondition) bool {
  276. condition.LastTransitionTime = metav1.Now()
  277. // Try to find this pod condition.
  278. conditionIndex, oldCondition := GetPodCondition(status, condition.Type)
  279. if oldCondition == nil {
  280. // We are adding new pod condition.
  281. status.Conditions = append(status.Conditions, *condition)
  282. return true
  283. }
  284. // We are updating an existing condition, so we need to check if it has changed.
  285. if condition.Status == oldCondition.Status {
  286. condition.LastTransitionTime = oldCondition.LastTransitionTime
  287. }
  288. isEqual := condition.Status == oldCondition.Status &&
  289. condition.Reason == oldCondition.Reason &&
  290. condition.Message == oldCondition.Message &&
  291. condition.LastProbeTime.Equal(&oldCondition.LastProbeTime) &&
  292. condition.LastTransitionTime.Equal(&oldCondition.LastTransitionTime)
  293. status.Conditions[conditionIndex] = *condition
  294. // Return true if one of the fields have changed.
  295. return !isEqual
  296. }
  297. // GetPodPriority returns priority of the given pod.
  298. func GetPodPriority(pod *v1.Pod) int32 {
  299. if pod.Spec.Priority != nil {
  300. return *pod.Spec.Priority
  301. }
  302. // When priority of a running pod is nil, it means it was created at a time
  303. // that there was no global default priority class and the priority class
  304. // name of the pod was empty. So, we resolve to the static default priority.
  305. return 0
  306. }