util.go 9.9 KB

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