1/* 2Copyright 2015 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package pod 18 19import ( 20 "fmt" 21 "time" 22 23 "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. 32func 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// Visitor is called with each object name, and returns true if visiting should continue 52type Visitor func(name string) (shouldContinue bool) 53 54// VisitPodSecretNames invokes the visitor function with the name of every secret 55// referenced by the pod spec. If visitor returns false, visiting is short-circuited. 56// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. 57// Returns true if visiting completed, false if visiting was short-circuited. 58func VisitPodSecretNames(pod *v1.Pod, visitor Visitor) bool { 59 for _, reference := range pod.Spec.ImagePullSecrets { 60 if !visitor(reference.Name) { 61 return false 62 } 63 } 64 for i := range pod.Spec.InitContainers { 65 if !visitContainerSecretNames(&pod.Spec.InitContainers[i], visitor) { 66 return false 67 } 68 } 69 for i := range pod.Spec.Containers { 70 if !visitContainerSecretNames(&pod.Spec.Containers[i], visitor) { 71 return false 72 } 73 } 74 var source *v1.VolumeSource 75 76 for i := range pod.Spec.Volumes { 77 source = &pod.Spec.Volumes[i].VolumeSource 78 switch { 79 case source.AzureFile != nil: 80 if len(source.AzureFile.SecretName) > 0 && !visitor(source.AzureFile.SecretName) { 81 return false 82 } 83 case source.CephFS != nil: 84 if source.CephFS.SecretRef != nil && !visitor(source.CephFS.SecretRef.Name) { 85 return false 86 } 87 case source.Cinder != nil: 88 if source.Cinder.SecretRef != nil && !visitor(source.Cinder.SecretRef.Name) { 89 return false 90 } 91 case source.FlexVolume != nil: 92 if source.FlexVolume.SecretRef != nil && !visitor(source.FlexVolume.SecretRef.Name) { 93 return false 94 } 95 case source.Projected != nil: 96 for j := range source.Projected.Sources { 97 if source.Projected.Sources[j].Secret != nil { 98 if !visitor(source.Projected.Sources[j].Secret.Name) { 99 return false 100 } 101 } 102 } 103 case source.RBD != nil: 104 if source.RBD.SecretRef != nil && !visitor(source.RBD.SecretRef.Name) { 105 return false 106 } 107 case source.Secret != nil: 108 if !visitor(source.Secret.SecretName) { 109 return false 110 } 111 case source.ScaleIO != nil: 112 if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) { 113 return false 114 } 115 case source.ISCSI != nil: 116 if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) { 117 return false 118 } 119 case source.StorageOS != nil: 120 if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) { 121 return false 122 } 123 } 124 } 125 return true 126} 127 128func visitContainerSecretNames(container *v1.Container, visitor Visitor) bool { 129 for _, env := range container.EnvFrom { 130 if env.SecretRef != nil { 131 if !visitor(env.SecretRef.Name) { 132 return false 133 } 134 } 135 } 136 for _, envVar := range container.Env { 137 if envVar.ValueFrom != nil && envVar.ValueFrom.SecretKeyRef != nil { 138 if !visitor(envVar.ValueFrom.SecretKeyRef.Name) { 139 return false 140 } 141 } 142 } 143 return true 144} 145 146// VisitPodConfigmapNames invokes the visitor function with the name of every configmap 147// referenced by the pod spec. If visitor returns false, visiting is short-circuited. 148// Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. 149// Returns true if visiting completed, false if visiting was short-circuited. 150func VisitPodConfigmapNames(pod *v1.Pod, visitor Visitor) bool { 151 for i := range pod.Spec.InitContainers { 152 if !visitContainerConfigmapNames(&pod.Spec.InitContainers[i], visitor) { 153 return false 154 } 155 } 156 for i := range pod.Spec.Containers { 157 if !visitContainerConfigmapNames(&pod.Spec.Containers[i], visitor) { 158 return false 159 } 160 } 161 var source *v1.VolumeSource 162 for i := range pod.Spec.Volumes { 163 source = &pod.Spec.Volumes[i].VolumeSource 164 switch { 165 case source.Projected != nil: 166 for j := range source.Projected.Sources { 167 if source.Projected.Sources[j].ConfigMap != nil { 168 if !visitor(source.Projected.Sources[j].ConfigMap.Name) { 169 return false 170 } 171 } 172 } 173 case source.ConfigMap != nil: 174 if !visitor(source.ConfigMap.Name) { 175 return false 176 } 177 } 178 } 179 return true 180} 181 182func visitContainerConfigmapNames(container *v1.Container, visitor Visitor) bool { 183 for _, env := range container.EnvFrom { 184 if env.ConfigMapRef != nil { 185 if !visitor(env.ConfigMapRef.Name) { 186 return false 187 } 188 } 189 } 190 for _, envVar := range container.Env { 191 if envVar.ValueFrom != nil && envVar.ValueFrom.ConfigMapKeyRef != nil { 192 if !visitor(envVar.ValueFrom.ConfigMapKeyRef.Name) { 193 return false 194 } 195 } 196 } 197 return true 198} 199 200// GetContainerStatus extracts the status of container "name" from "statuses". 201// It also returns if "name" exists. 202func GetContainerStatus(statuses []v1.ContainerStatus, name string) (v1.ContainerStatus, bool) { 203 for i := range statuses { 204 if statuses[i].Name == name { 205 return statuses[i], true 206 } 207 } 208 return v1.ContainerStatus{}, false 209} 210 211// GetExistingContainerStatus extracts the status of container "name" from "statuses", 212// and returns empty status if "name" does not exist. 213func GetExistingContainerStatus(statuses []v1.ContainerStatus, name string) v1.ContainerStatus { 214 for i := range statuses { 215 if statuses[i].Name == name { 216 return statuses[i] 217 } 218 } 219 return v1.ContainerStatus{} 220} 221 222// IsPodAvailable returns true if a pod is available; false otherwise. 223// Precondition for an available pod is that it must be ready. On top 224// of that, there are two cases when a pod can be considered available: 225// 1. minReadySeconds == 0, or 226// 2. LastTransitionTime (is set) + minReadySeconds < current time 227func IsPodAvailable(pod *v1.Pod, minReadySeconds int32, now metav1.Time) bool { 228 if !IsPodReady(pod) { 229 return false 230 } 231 232 c := GetPodReadyCondition(pod.Status) 233 minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second 234 if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) { 235 return true 236 } 237 return false 238} 239 240// IsPodReady returns true if a pod is ready; false otherwise. 241func IsPodReady(pod *v1.Pod) bool { 242 return IsPodReadyConditionTrue(pod.Status) 243} 244 245// IsPodReady returns true if a pod is ready; false otherwise. 246func IsPodReadyConditionTrue(status v1.PodStatus) bool { 247 condition := GetPodReadyCondition(status) 248 return condition != nil && condition.Status == v1.ConditionTrue 249} 250 251// Extracts the pod ready condition from the given status and returns that. 252// Returns nil if the condition is not present. 253func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition { 254 _, condition := GetPodCondition(&status, v1.PodReady) 255 return condition 256} 257 258// GetPodCondition extracts the provided condition from the given status and returns that. 259// Returns nil and -1 if the condition is not present, and the index of the located condition. 260func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) { 261 if status == nil { 262 return -1, nil 263 } 264 return GetPodConditionFromList(status.Conditions, conditionType) 265} 266 267// GetPodConditionFromList extracts the provided condition from the given list of condition and 268// returns the index of the condition and the condition. Returns -1 and nil if the condition is not present. 269func GetPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) { 270 if conditions == nil { 271 return -1, nil 272 } 273 for i := range conditions { 274 if conditions[i].Type == conditionType { 275 return i, &conditions[i] 276 } 277 } 278 return -1, nil 279} 280 281// Updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the 282// status has changed. 283// Returns true if pod condition has changed or has been added. 284func UpdatePodCondition(status *v1.PodStatus, condition *v1.PodCondition) bool { 285 condition.LastTransitionTime = metav1.Now() 286 // Try to find this pod condition. 287 conditionIndex, oldCondition := GetPodCondition(status, condition.Type) 288 289 if oldCondition == nil { 290 // We are adding new pod condition. 291 status.Conditions = append(status.Conditions, *condition) 292 return true 293 } else { 294 // We are updating an existing condition, so we need to check if it has changed. 295 if condition.Status == oldCondition.Status { 296 condition.LastTransitionTime = oldCondition.LastTransitionTime 297 } 298 299 isEqual := condition.Status == oldCondition.Status && 300 condition.Reason == oldCondition.Reason && 301 condition.Message == oldCondition.Message && 302 condition.LastProbeTime.Equal(&oldCondition.LastProbeTime) && 303 condition.LastTransitionTime.Equal(&oldCondition.LastTransitionTime) 304 305 status.Conditions[conditionIndex] = *condition 306 // Return true if one of the fields have changed. 307 return !isEqual 308 } 309} 310