k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/api/v1/pod/util.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package pod 18 19 import ( 20 "fmt" 21 "time" 22 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/util/intstr" 26 ) 27 28 // FindPort locates the container port for the given pod and portName. If the 29 // targetPort is a number, use that. If the targetPort is a string, look that 30 // string up in all named ports in all containers in the target pod. If no 31 // match is found, fail. 32 func FindPort(pod *v1.Pod, svcPort *v1.ServicePort) (int, error) { 33 portName := svcPort.TargetPort 34 switch portName.Type { 35 case intstr.String: 36 name := portName.StrVal 37 for _, container := range pod.Spec.Containers { 38 for _, port := range container.Ports { 39 if port.Name == name && port.Protocol == svcPort.Protocol { 40 return int(port.ContainerPort), nil 41 } 42 } 43 } 44 case intstr.Int: 45 return portName.IntValue(), nil 46 } 47 48 return 0, fmt.Errorf("no suitable port for manifest: %s", pod.UID) 49 } 50 51 // ContainerType signifies container type 52 type ContainerType int 53 54 const ( 55 // Containers is for normal containers 56 Containers ContainerType = 1 << iota 57 // InitContainers is for init containers 58 InitContainers 59 // EphemeralContainers is for ephemeral containers 60 EphemeralContainers 61 ) 62 63 // AllContainers specifies that all containers be visited 64 const AllContainers ContainerType = InitContainers | Containers | EphemeralContainers 65 66 // AllFeatureEnabledContainers returns a ContainerType mask which includes all container 67 // types except for the ones guarded by feature gate. 68 func AllFeatureEnabledContainers() ContainerType { 69 return AllContainers 70 } 71 72 // ContainerVisitor is called with each container spec, and returns true 73 // if visiting should continue. 74 type ContainerVisitor func(container *v1.Container, containerType ContainerType) (shouldContinue bool) 75 76 // Visitor is called with each object name, and returns true if visiting should continue 77 type Visitor func(name string) (shouldContinue bool) 78 79 func skipEmptyNames(visitor Visitor) Visitor { 80 return func(name string) bool { 81 if len(name) == 0 { 82 // continue visiting 83 return true 84 } 85 // delegate to visitor 86 return visitor(name) 87 } 88 } 89 90 // VisitContainers invokes the visitor function with a pointer to every container 91 // spec in the given pod spec with type set in mask. If visitor returns false, 92 // visiting is short-circuited. VisitContainers returns true if visiting completes, 93 // false if visiting was short-circuited. 94 func VisitContainers(podSpec *v1.PodSpec, mask ContainerType, visitor ContainerVisitor) bool { 95 if mask&InitContainers != 0 { 96 for i := range podSpec.InitContainers { 97 if !visitor(&podSpec.InitContainers[i], InitContainers) { 98 return false 99 } 100 } 101 } 102 if mask&Containers != 0 { 103 for i := range podSpec.Containers { 104 if !visitor(&podSpec.Containers[i], Containers) { 105 return false 106 } 107 } 108 } 109 if mask&EphemeralContainers != 0 { 110 for i := range podSpec.EphemeralContainers { 111 if !visitor((*v1.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), EphemeralContainers) { 112 return false 113 } 114 } 115 } 116 return true 117 } 118 119 // VisitPodSecretNames invokes the visitor function with the name of every secret 120 // referenced by the pod spec. If visitor returns false, visiting is short-circuited. 121 // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. 122 // Returns true if visiting completed, false if visiting was short-circuited. 123 func VisitPodSecretNames(pod *v1.Pod, visitor Visitor) bool { 124 visitor = skipEmptyNames(visitor) 125 for _, reference := range pod.Spec.ImagePullSecrets { 126 if !visitor(reference.Name) { 127 return false 128 } 129 } 130 VisitContainers(&pod.Spec, AllContainers, func(c *v1.Container, containerType ContainerType) bool { 131 return visitContainerSecretNames(c, visitor) 132 }) 133 var source *v1.VolumeSource 134 135 for i := range pod.Spec.Volumes { 136 source = &pod.Spec.Volumes[i].VolumeSource 137 switch { 138 case source.AzureFile != nil: 139 if len(source.AzureFile.SecretName) > 0 && !visitor(source.AzureFile.SecretName) { 140 return false 141 } 142 case source.CephFS != nil: 143 if source.CephFS.SecretRef != nil && !visitor(source.CephFS.SecretRef.Name) { 144 return false 145 } 146 case source.Cinder != nil: 147 if source.Cinder.SecretRef != nil && !visitor(source.Cinder.SecretRef.Name) { 148 return false 149 } 150 case source.FlexVolume != nil: 151 if source.FlexVolume.SecretRef != nil && !visitor(source.FlexVolume.SecretRef.Name) { 152 return false 153 } 154 case source.Projected != nil: 155 for j := range source.Projected.Sources { 156 if source.Projected.Sources[j].Secret != nil { 157 if !visitor(source.Projected.Sources[j].Secret.Name) { 158 return false 159 } 160 } 161 } 162 case source.RBD != nil: 163 if source.RBD.SecretRef != nil && !visitor(source.RBD.SecretRef.Name) { 164 return false 165 } 166 case source.Secret != nil: 167 if !visitor(source.Secret.SecretName) { 168 return false 169 } 170 case source.ScaleIO != nil: 171 if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) { 172 return false 173 } 174 case source.ISCSI != nil: 175 if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) { 176 return false 177 } 178 case source.StorageOS != nil: 179 if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) { 180 return false 181 } 182 case source.CSI != nil: 183 if source.CSI.NodePublishSecretRef != nil && !visitor(source.CSI.NodePublishSecretRef.Name) { 184 return false 185 } 186 } 187 } 188 return true 189 } 190 191 // visitContainerSecretNames returns true unless the visitor returned false when invoked with a secret reference 192 func visitContainerSecretNames(container *v1.Container, visitor Visitor) bool { 193 for _, env := range container.EnvFrom { 194 if env.SecretRef != nil { 195 if !visitor(env.SecretRef.Name) { 196 return false 197 } 198 } 199 } 200 for _, envVar := range container.Env { 201 if envVar.ValueFrom != nil && envVar.ValueFrom.SecretKeyRef != nil { 202 if !visitor(envVar.ValueFrom.SecretKeyRef.Name) { 203 return false 204 } 205 } 206 } 207 return true 208 } 209 210 // VisitPodConfigmapNames invokes the visitor function with the name of every configmap 211 // referenced by the pod spec. If visitor returns false, visiting is short-circuited. 212 // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. 213 // Returns true if visiting completed, false if visiting was short-circuited. 214 func VisitPodConfigmapNames(pod *v1.Pod, visitor Visitor) bool { 215 visitor = skipEmptyNames(visitor) 216 VisitContainers(&pod.Spec, AllContainers, func(c *v1.Container, containerType ContainerType) bool { 217 return visitContainerConfigmapNames(c, visitor) 218 }) 219 var source *v1.VolumeSource 220 for i := range pod.Spec.Volumes { 221 source = &pod.Spec.Volumes[i].VolumeSource 222 switch { 223 case source.Projected != nil: 224 for j := range source.Projected.Sources { 225 if source.Projected.Sources[j].ConfigMap != nil { 226 if !visitor(source.Projected.Sources[j].ConfigMap.Name) { 227 return false 228 } 229 } 230 } 231 case source.ConfigMap != nil: 232 if !visitor(source.ConfigMap.Name) { 233 return false 234 } 235 } 236 } 237 return true 238 } 239 240 // visitContainerConfigmapNames returns true unless the visitor returned false when invoked with a configmap reference 241 func visitContainerConfigmapNames(container *v1.Container, visitor Visitor) bool { 242 for _, env := range container.EnvFrom { 243 if env.ConfigMapRef != nil { 244 if !visitor(env.ConfigMapRef.Name) { 245 return false 246 } 247 } 248 } 249 for _, envVar := range container.Env { 250 if envVar.ValueFrom != nil && envVar.ValueFrom.ConfigMapKeyRef != nil { 251 if !visitor(envVar.ValueFrom.ConfigMapKeyRef.Name) { 252 return false 253 } 254 } 255 } 256 return true 257 } 258 259 // GetContainerStatus extracts the status of container "name" from "statuses". 260 // It returns true if "name" exists, else returns false. 261 func GetContainerStatus(statuses []v1.ContainerStatus, name string) (v1.ContainerStatus, bool) { 262 for i := range statuses { 263 if statuses[i].Name == name { 264 return statuses[i], true 265 } 266 } 267 return v1.ContainerStatus{}, false 268 } 269 270 // GetExistingContainerStatus extracts the status of container "name" from "statuses", 271 // It also returns if "name" exists. 272 func GetExistingContainerStatus(statuses []v1.ContainerStatus, name string) v1.ContainerStatus { 273 status, _ := GetContainerStatus(statuses, name) 274 return status 275 } 276 277 // GetIndexOfContainerStatus gets the index of status of container "name" from "statuses", 278 // It returns (index, true) if "name" exists, else returns (0, false). 279 func GetIndexOfContainerStatus(statuses []v1.ContainerStatus, name string) (int, bool) { 280 for i := range statuses { 281 if statuses[i].Name == name { 282 return i, true 283 } 284 } 285 return 0, false 286 } 287 288 // IsPodAvailable returns true if a pod is available; false otherwise. 289 // Precondition for an available pod is that it must be ready. On top 290 // of that, there are two cases when a pod can be considered available: 291 // 1. minReadySeconds == 0, or 292 // 2. LastTransitionTime (is set) + minReadySeconds < current time 293 func IsPodAvailable(pod *v1.Pod, minReadySeconds int32, now metav1.Time) bool { 294 if !IsPodReady(pod) { 295 return false 296 } 297 298 c := GetPodReadyCondition(pod.Status) 299 minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second 300 if minReadySeconds == 0 || (!c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time)) { 301 return true 302 } 303 return false 304 } 305 306 // IsPodReady returns true if a pod is ready; false otherwise. 307 func IsPodReady(pod *v1.Pod) bool { 308 return IsPodReadyConditionTrue(pod.Status) 309 } 310 311 // IsPodTerminal returns true if a pod is terminal, all containers are stopped and cannot ever regress. 312 func IsPodTerminal(pod *v1.Pod) bool { 313 return IsPodPhaseTerminal(pod.Status.Phase) 314 } 315 316 // IsPodPhaseTerminal returns true if the pod's phase is terminal. 317 func IsPodPhaseTerminal(phase v1.PodPhase) bool { 318 return phase == v1.PodFailed || phase == v1.PodSucceeded 319 } 320 321 // IsPodReadyConditionTrue returns true if a pod is ready; false otherwise. 322 func IsPodReadyConditionTrue(status v1.PodStatus) bool { 323 condition := GetPodReadyCondition(status) 324 return condition != nil && condition.Status == v1.ConditionTrue 325 } 326 327 // IsContainersReadyConditionTrue returns true if a pod is ready; false otherwise. 328 func IsContainersReadyConditionTrue(status v1.PodStatus) bool { 329 condition := GetContainersReadyCondition(status) 330 return condition != nil && condition.Status == v1.ConditionTrue 331 } 332 333 // GetPodReadyCondition extracts the pod ready condition from the given status and returns that. 334 // Returns nil if the condition is not present. 335 func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition { 336 _, condition := GetPodCondition(&status, v1.PodReady) 337 return condition 338 } 339 340 // GetContainersReadyCondition extracts the containers ready condition from the given status and returns that. 341 // Returns nil if the condition is not present. 342 func GetContainersReadyCondition(status v1.PodStatus) *v1.PodCondition { 343 _, condition := GetPodCondition(&status, v1.ContainersReady) 344 return condition 345 } 346 347 // GetPodCondition extracts the provided condition from the given status and returns that. 348 // Returns nil and -1 if the condition is not present, and the index of the located condition. 349 func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) { 350 if status == nil { 351 return -1, nil 352 } 353 return GetPodConditionFromList(status.Conditions, conditionType) 354 } 355 356 // GetPodConditionFromList extracts the provided condition from the given list of condition and 357 // returns the index of the condition and the condition. Returns -1 and nil if the condition is not present. 358 func GetPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) { 359 if conditions == nil { 360 return -1, nil 361 } 362 for i := range conditions { 363 if conditions[i].Type == conditionType { 364 return i, &conditions[i] 365 } 366 } 367 return -1, nil 368 } 369 370 // UpdatePodCondition updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the 371 // status has changed. 372 // Returns true if pod condition has changed or has been added. 373 func UpdatePodCondition(status *v1.PodStatus, condition *v1.PodCondition) bool { 374 condition.LastTransitionTime = metav1.Now() 375 // Try to find this pod condition. 376 conditionIndex, oldCondition := GetPodCondition(status, condition.Type) 377 378 if oldCondition == nil { 379 // We are adding new pod condition. 380 status.Conditions = append(status.Conditions, *condition) 381 return true 382 } 383 // We are updating an existing condition, so we need to check if it has changed. 384 if condition.Status == oldCondition.Status { 385 condition.LastTransitionTime = oldCondition.LastTransitionTime 386 } 387 388 isEqual := condition.Status == oldCondition.Status && 389 condition.Reason == oldCondition.Reason && 390 condition.Message == oldCondition.Message && 391 condition.LastProbeTime.Equal(&oldCondition.LastProbeTime) && 392 condition.LastTransitionTime.Equal(&oldCondition.LastTransitionTime) 393 394 status.Conditions[conditionIndex] = *condition 395 // Return true if one of the fields have changed. 396 return !isEqual 397 }