k8s.io/kubernetes@v1.29.3/pkg/api/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 "strings" 21 22 "github.com/google/go-cmp/cmp" 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 metavalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" 26 utilfeature "k8s.io/apiserver/pkg/util/feature" 27 api "k8s.io/kubernetes/pkg/apis/core" 28 "k8s.io/kubernetes/pkg/apis/core/helper" 29 apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" 30 "k8s.io/kubernetes/pkg/features" 31 ) 32 33 // ContainerType signifies container type 34 type ContainerType int 35 36 const ( 37 // Containers is for normal containers 38 Containers ContainerType = 1 << iota 39 // InitContainers is for init containers 40 InitContainers 41 // EphemeralContainers is for ephemeral containers 42 EphemeralContainers 43 ) 44 45 // AllContainers specifies that all containers be visited 46 const AllContainers ContainerType = (InitContainers | Containers | EphemeralContainers) 47 48 // AllFeatureEnabledContainers returns a ContainerType mask which includes all container 49 // types except for the ones guarded by feature gate. 50 func AllFeatureEnabledContainers() ContainerType { 51 return AllContainers 52 } 53 54 // ContainerVisitor is called with each container spec, and returns true 55 // if visiting should continue. 56 type ContainerVisitor func(container *api.Container, containerType ContainerType) (shouldContinue bool) 57 58 // VisitContainers invokes the visitor function with a pointer to every container 59 // spec in the given pod spec with type set in mask. If visitor returns false, 60 // visiting is short-circuited. VisitContainers returns true if visiting completes, 61 // false if visiting was short-circuited. 62 func VisitContainers(podSpec *api.PodSpec, mask ContainerType, visitor ContainerVisitor) bool { 63 if mask&InitContainers != 0 { 64 for i := range podSpec.InitContainers { 65 if !visitor(&podSpec.InitContainers[i], InitContainers) { 66 return false 67 } 68 } 69 } 70 if mask&Containers != 0 { 71 for i := range podSpec.Containers { 72 if !visitor(&podSpec.Containers[i], Containers) { 73 return false 74 } 75 } 76 } 77 if mask&EphemeralContainers != 0 { 78 for i := range podSpec.EphemeralContainers { 79 if !visitor((*api.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), EphemeralContainers) { 80 return false 81 } 82 } 83 } 84 return true 85 } 86 87 // Visitor is called with each object name, and returns true if visiting should continue 88 type Visitor func(name string) (shouldContinue bool) 89 90 func skipEmptyNames(visitor Visitor) Visitor { 91 return func(name string) bool { 92 if len(name) == 0 { 93 // continue visiting 94 return true 95 } 96 // delegate to visitor 97 return visitor(name) 98 } 99 } 100 101 // VisitPodSecretNames invokes the visitor function with the name of every secret 102 // referenced by the pod spec. If visitor returns false, visiting is short-circuited. 103 // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. 104 // Returns true if visiting completed, false if visiting was short-circuited. 105 func VisitPodSecretNames(pod *api.Pod, visitor Visitor, containerType ContainerType) bool { 106 visitor = skipEmptyNames(visitor) 107 for _, reference := range pod.Spec.ImagePullSecrets { 108 if !visitor(reference.Name) { 109 return false 110 } 111 } 112 VisitContainers(&pod.Spec, containerType, func(c *api.Container, containerType ContainerType) bool { 113 return visitContainerSecretNames(c, visitor) 114 }) 115 var source *api.VolumeSource 116 for i := range pod.Spec.Volumes { 117 source = &pod.Spec.Volumes[i].VolumeSource 118 switch { 119 case source.AzureFile != nil: 120 if len(source.AzureFile.SecretName) > 0 && !visitor(source.AzureFile.SecretName) { 121 return false 122 } 123 case source.CephFS != nil: 124 if source.CephFS.SecretRef != nil && !visitor(source.CephFS.SecretRef.Name) { 125 return false 126 } 127 case source.Cinder != nil: 128 if source.Cinder.SecretRef != nil && !visitor(source.Cinder.SecretRef.Name) { 129 return false 130 } 131 case source.FlexVolume != nil: 132 if source.FlexVolume.SecretRef != nil && !visitor(source.FlexVolume.SecretRef.Name) { 133 return false 134 } 135 case source.Projected != nil: 136 for j := range source.Projected.Sources { 137 if source.Projected.Sources[j].Secret != nil { 138 if !visitor(source.Projected.Sources[j].Secret.Name) { 139 return false 140 } 141 } 142 } 143 case source.RBD != nil: 144 if source.RBD.SecretRef != nil && !visitor(source.RBD.SecretRef.Name) { 145 return false 146 } 147 case source.Secret != nil: 148 if !visitor(source.Secret.SecretName) { 149 return false 150 } 151 case source.ScaleIO != nil: 152 if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) { 153 return false 154 } 155 case source.ISCSI != nil: 156 if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) { 157 return false 158 } 159 case source.StorageOS != nil: 160 if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) { 161 return false 162 } 163 case source.CSI != nil: 164 if source.CSI.NodePublishSecretRef != nil && !visitor(source.CSI.NodePublishSecretRef.Name) { 165 return false 166 } 167 } 168 } 169 return true 170 } 171 172 func visitContainerSecretNames(container *api.Container, visitor Visitor) bool { 173 for _, env := range container.EnvFrom { 174 if env.SecretRef != nil { 175 if !visitor(env.SecretRef.Name) { 176 return false 177 } 178 } 179 } 180 for _, envVar := range container.Env { 181 if envVar.ValueFrom != nil && envVar.ValueFrom.SecretKeyRef != nil { 182 if !visitor(envVar.ValueFrom.SecretKeyRef.Name) { 183 return false 184 } 185 } 186 } 187 return true 188 } 189 190 // VisitPodConfigmapNames invokes the visitor function with the name of every configmap 191 // referenced by the pod spec. If visitor returns false, visiting is short-circuited. 192 // Transitive references (e.g. pod -> pvc -> pv -> secret) are not visited. 193 // Returns true if visiting completed, false if visiting was short-circuited. 194 func VisitPodConfigmapNames(pod *api.Pod, visitor Visitor, containerType ContainerType) bool { 195 visitor = skipEmptyNames(visitor) 196 VisitContainers(&pod.Spec, containerType, func(c *api.Container, containerType ContainerType) bool { 197 return visitContainerConfigmapNames(c, visitor) 198 }) 199 var source *api.VolumeSource 200 for i := range pod.Spec.Volumes { 201 source = &pod.Spec.Volumes[i].VolumeSource 202 switch { 203 case source.Projected != nil: 204 for j := range source.Projected.Sources { 205 if source.Projected.Sources[j].ConfigMap != nil { 206 if !visitor(source.Projected.Sources[j].ConfigMap.Name) { 207 return false 208 } 209 } 210 } 211 case source.ConfigMap != nil: 212 if !visitor(source.ConfigMap.Name) { 213 return false 214 } 215 } 216 } 217 return true 218 } 219 220 func visitContainerConfigmapNames(container *api.Container, visitor Visitor) bool { 221 for _, env := range container.EnvFrom { 222 if env.ConfigMapRef != nil { 223 if !visitor(env.ConfigMapRef.Name) { 224 return false 225 } 226 } 227 } 228 for _, envVar := range container.Env { 229 if envVar.ValueFrom != nil && envVar.ValueFrom.ConfigMapKeyRef != nil { 230 if !visitor(envVar.ValueFrom.ConfigMapKeyRef.Name) { 231 return false 232 } 233 } 234 } 235 return true 236 } 237 238 // IsPodReady returns true if a pod is ready; false otherwise. 239 func IsPodReady(pod *api.Pod) bool { 240 return IsPodReadyConditionTrue(pod.Status) 241 } 242 243 // IsPodReadyConditionTrue returns true if a pod is ready; false otherwise. 244 func IsPodReadyConditionTrue(status api.PodStatus) bool { 245 condition := GetPodReadyCondition(status) 246 return condition != nil && condition.Status == api.ConditionTrue 247 } 248 249 // GetPodReadyCondition extracts the pod ready condition from the given status and returns that. 250 // Returns nil if the condition is not present. 251 func GetPodReadyCondition(status api.PodStatus) *api.PodCondition { 252 _, condition := GetPodCondition(&status, api.PodReady) 253 return condition 254 } 255 256 // GetPodCondition extracts the provided condition from the given status and returns that. 257 // Returns nil and -1 if the condition is not present, and the index of the located condition. 258 func GetPodCondition(status *api.PodStatus, conditionType api.PodConditionType) (int, *api.PodCondition) { 259 if status == nil { 260 return -1, nil 261 } 262 for i := range status.Conditions { 263 if status.Conditions[i].Type == conditionType { 264 return i, &status.Conditions[i] 265 } 266 } 267 return -1, nil 268 } 269 270 // UpdatePodCondition updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the 271 // status has changed. 272 // Returns true if pod condition has changed or has been added. 273 func UpdatePodCondition(status *api.PodStatus, condition *api.PodCondition) bool { 274 condition.LastTransitionTime = metav1.Now() 275 // Try to find this pod condition. 276 conditionIndex, oldCondition := GetPodCondition(status, condition.Type) 277 278 if oldCondition == nil { 279 // We are adding new pod condition. 280 status.Conditions = append(status.Conditions, *condition) 281 return true 282 } 283 // We are updating an existing condition, so we need to check if it has changed. 284 if condition.Status == oldCondition.Status { 285 condition.LastTransitionTime = oldCondition.LastTransitionTime 286 } 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 294 status.Conditions[conditionIndex] = *condition 295 // Return true if one of the fields have changed. 296 return !isEqual 297 } 298 299 func checkContainerUseIndivisibleHugePagesValues(container api.Container) bool { 300 for resourceName, quantity := range container.Resources.Limits { 301 if helper.IsHugePageResourceName(resourceName) { 302 if !helper.IsHugePageResourceValueDivisible(resourceName, quantity) { 303 return true 304 } 305 } 306 } 307 308 for resourceName, quantity := range container.Resources.Requests { 309 if helper.IsHugePageResourceName(resourceName) { 310 if !helper.IsHugePageResourceValueDivisible(resourceName, quantity) { 311 return true 312 } 313 } 314 } 315 316 return false 317 } 318 319 // usesIndivisibleHugePagesValues returns true if the one of the containers uses non-integer multiple 320 // of huge page unit size 321 func usesIndivisibleHugePagesValues(podSpec *api.PodSpec) bool { 322 foundIndivisibleHugePagesValue := false 323 VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool { 324 if checkContainerUseIndivisibleHugePagesValues(*c) { 325 foundIndivisibleHugePagesValue = true 326 } 327 return !foundIndivisibleHugePagesValue // continue visiting if we haven't seen an invalid value yet 328 }) 329 330 if foundIndivisibleHugePagesValue { 331 return true 332 } 333 334 for resourceName, quantity := range podSpec.Overhead { 335 if helper.IsHugePageResourceName(resourceName) { 336 if !helper.IsHugePageResourceValueDivisible(resourceName, quantity) { 337 return true 338 } 339 } 340 } 341 342 return false 343 } 344 345 // hasInvalidTopologySpreadConstraintLabelSelector return true if spec.TopologySpreadConstraints have any entry with invalid labelSelector 346 func hasInvalidTopologySpreadConstraintLabelSelector(spec *api.PodSpec) bool { 347 for _, constraint := range spec.TopologySpreadConstraints { 348 errs := metavalidation.ValidateLabelSelector(constraint.LabelSelector, metavalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, nil) 349 if len(errs) != 0 { 350 return true 351 } 352 } 353 return false 354 } 355 356 // GetValidationOptionsFromPodSpecAndMeta returns validation options based on pod specs and metadata 357 func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, podMeta, oldPodMeta *metav1.ObjectMeta) apivalidation.PodValidationOptions { 358 // default pod validation options based on feature gate 359 opts := apivalidation.PodValidationOptions{ 360 AllowInvalidPodDeletionCost: !utilfeature.DefaultFeatureGate.Enabled(features.PodDeletionCost), 361 // Allow pod spec to use status.hostIPs in downward API if feature is enabled 362 AllowHostIPsField: utilfeature.DefaultFeatureGate.Enabled(features.PodHostIPs), 363 // Do not allow pod spec to use non-integer multiple of huge page unit size default 364 AllowIndivisibleHugePagesValues: false, 365 AllowInvalidLabelValueInSelector: false, 366 AllowInvalidTopologySpreadConstraintLabelSelector: false, 367 AllowMutableNodeSelectorAndNodeAffinity: utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness), 368 AllowNamespacedSysctlsForHostNetAndHostIPC: false, 369 } 370 371 if oldPodSpec != nil { 372 // if old spec has status.hostIPs downwardAPI set, we must allow it 373 opts.AllowHostIPsField = opts.AllowHostIPsField || hasUsedDownwardAPIFieldPathWithPodSpec(oldPodSpec, "status.hostIPs") 374 375 // if old spec used non-integer multiple of huge page unit size, we must allow it 376 opts.AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues(oldPodSpec) 377 378 opts.AllowInvalidLabelValueInSelector = hasInvalidLabelValueInAffinitySelector(oldPodSpec) 379 // if old spec has invalid labelSelector in topologySpreadConstraint, we must allow it 380 opts.AllowInvalidTopologySpreadConstraintLabelSelector = hasInvalidTopologySpreadConstraintLabelSelector(oldPodSpec) 381 382 // if old spec has invalid sysctl with hostNet or hostIPC, we must allow it when update 383 if oldPodSpec.SecurityContext != nil && len(oldPodSpec.SecurityContext.Sysctls) != 0 { 384 for _, s := range oldPodSpec.SecurityContext.Sysctls { 385 err := apivalidation.ValidateHostSysctl(s.Name, oldPodSpec.SecurityContext, nil) 386 if err != nil { 387 opts.AllowNamespacedSysctlsForHostNetAndHostIPC = true 388 break 389 } 390 } 391 } 392 } 393 if oldPodMeta != nil && !opts.AllowInvalidPodDeletionCost { 394 // This is an update, so validate only if the existing object was valid. 395 _, err := helper.GetDeletionCostFromPodAnnotations(oldPodMeta.Annotations) 396 opts.AllowInvalidPodDeletionCost = err != nil 397 } 398 399 return opts 400 } 401 402 func hasUsedDownwardAPIFieldPathWithPodSpec(podSpec *api.PodSpec, fieldPath string) bool { 403 if podSpec == nil { 404 return false 405 } 406 for _, vol := range podSpec.Volumes { 407 if hasUsedDownwardAPIFieldPathWithVolume(&vol, fieldPath) { 408 return true 409 } 410 } 411 for _, c := range podSpec.InitContainers { 412 if hasUsedDownwardAPIFieldPathWithContainer(&c, fieldPath) { 413 return true 414 } 415 } 416 for _, c := range podSpec.Containers { 417 if hasUsedDownwardAPIFieldPathWithContainer(&c, fieldPath) { 418 return true 419 } 420 } 421 return false 422 } 423 424 func hasUsedDownwardAPIFieldPathWithVolume(volume *api.Volume, fieldPath string) bool { 425 if volume == nil || volume.DownwardAPI == nil { 426 return false 427 } 428 for _, file := range volume.DownwardAPI.Items { 429 if file.FieldRef != nil && 430 file.FieldRef.FieldPath == fieldPath { 431 return true 432 } 433 } 434 return false 435 } 436 437 func hasUsedDownwardAPIFieldPathWithContainer(container *api.Container, fieldPath string) bool { 438 if container == nil { 439 return false 440 } 441 for _, env := range container.Env { 442 if env.ValueFrom != nil && 443 env.ValueFrom.FieldRef != nil && 444 env.ValueFrom.FieldRef.FieldPath == fieldPath { 445 return true 446 } 447 } 448 return false 449 } 450 451 // GetValidationOptionsFromPodTemplate will return pod validation options for specified template. 452 func GetValidationOptionsFromPodTemplate(podTemplate, oldPodTemplate *api.PodTemplateSpec) apivalidation.PodValidationOptions { 453 var newPodSpec, oldPodSpec *api.PodSpec 454 var newPodMeta, oldPodMeta *metav1.ObjectMeta 455 // we have to be careful about nil pointers here 456 // replication controller in particular is prone to passing nil 457 if podTemplate != nil { 458 newPodSpec = &podTemplate.Spec 459 newPodMeta = &podTemplate.ObjectMeta 460 } 461 if oldPodTemplate != nil { 462 oldPodSpec = &oldPodTemplate.Spec 463 oldPodMeta = &oldPodTemplate.ObjectMeta 464 } 465 return GetValidationOptionsFromPodSpecAndMeta(newPodSpec, oldPodSpec, newPodMeta, oldPodMeta) 466 } 467 468 // DropDisabledTemplateFields removes disabled fields from the pod template metadata and spec. 469 // This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a PodTemplateSpec 470 func DropDisabledTemplateFields(podTemplate, oldPodTemplate *api.PodTemplateSpec) { 471 var ( 472 podSpec *api.PodSpec 473 podAnnotations map[string]string 474 oldPodSpec *api.PodSpec 475 oldPodAnnotations map[string]string 476 ) 477 if podTemplate != nil { 478 podSpec = &podTemplate.Spec 479 podAnnotations = podTemplate.Annotations 480 } 481 if oldPodTemplate != nil { 482 oldPodSpec = &oldPodTemplate.Spec 483 oldPodAnnotations = oldPodTemplate.Annotations 484 } 485 dropDisabledFields(podSpec, podAnnotations, oldPodSpec, oldPodAnnotations) 486 } 487 488 // DropDisabledPodFields removes disabled fields from the pod metadata and spec. 489 // This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a Pod 490 func DropDisabledPodFields(pod, oldPod *api.Pod) { 491 var ( 492 podSpec *api.PodSpec 493 podStatus *api.PodStatus 494 podAnnotations map[string]string 495 oldPodSpec *api.PodSpec 496 oldPodStatus *api.PodStatus 497 oldPodAnnotations map[string]string 498 ) 499 if pod != nil { 500 podSpec = &pod.Spec 501 podStatus = &pod.Status 502 podAnnotations = pod.Annotations 503 } 504 if oldPod != nil { 505 oldPodSpec = &oldPod.Spec 506 oldPodStatus = &oldPod.Status 507 oldPodAnnotations = oldPod.Annotations 508 } 509 dropDisabledFields(podSpec, podAnnotations, oldPodSpec, oldPodAnnotations) 510 dropDisabledPodStatusFields(podStatus, oldPodStatus, podSpec, oldPodSpec) 511 } 512 513 // dropDisabledFields removes disabled fields from the pod metadata and spec. 514 func dropDisabledFields( 515 podSpec *api.PodSpec, podAnnotations map[string]string, 516 oldPodSpec *api.PodSpec, oldPodAnnotations map[string]string, 517 ) { 518 // the new spec must always be non-nil 519 if podSpec == nil { 520 podSpec = &api.PodSpec{} 521 } 522 523 if !utilfeature.DefaultFeatureGate.Enabled(features.AppArmor) && !appArmorInUse(oldPodAnnotations) { 524 for k := range podAnnotations { 525 if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) { 526 delete(podAnnotations, k) 527 } 528 } 529 } 530 531 // If the feature is disabled and not in use, drop the hostUsers field. 532 if !utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesSupport) && !hostUsersInUse(oldPodSpec) { 533 // Drop the field in podSpec only if SecurityContext is not nil. 534 // If it is nil, there is no need to set hostUsers=nil (it will be nil too). 535 if podSpec.SecurityContext != nil { 536 podSpec.SecurityContext.HostUsers = nil 537 } 538 } 539 540 // If the feature is disabled and not in use, drop the schedulingGates field. 541 if !utilfeature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness) && !schedulingGatesInUse(oldPodSpec) { 542 podSpec.SchedulingGates = nil 543 } 544 545 dropDisabledProcMountField(podSpec, oldPodSpec) 546 547 dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec) 548 dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec) 549 dropDisabledMatchLabelKeysFieldInTopologySpread(podSpec, oldPodSpec) 550 dropDisabledMatchLabelKeysFieldInPodAffinity(podSpec, oldPodSpec) 551 dropDisabledDynamicResourceAllocationFields(podSpec, oldPodSpec) 552 dropDisabledClusterTrustBundleProjection(podSpec, oldPodSpec) 553 554 if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && !inPlacePodVerticalScalingInUse(oldPodSpec) { 555 // Drop ResizePolicy fields. Don't drop updates to Resources field as template.spec.resources 556 // field is mutable for certain controllers. Let ValidatePodUpdate handle it. 557 for i := range podSpec.Containers { 558 podSpec.Containers[i].ResizePolicy = nil 559 } 560 for i := range podSpec.InitContainers { 561 podSpec.InitContainers[i].ResizePolicy = nil 562 } 563 for i := range podSpec.EphemeralContainers { 564 podSpec.EphemeralContainers[i].ResizePolicy = nil 565 } 566 } 567 568 if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) && !restartableInitContainersInUse(oldPodSpec) { 569 // Drop the RestartPolicy field of init containers. 570 for i := range podSpec.InitContainers { 571 podSpec.InitContainers[i].RestartPolicy = nil 572 } 573 // For other types of containers, validateContainers will handle them. 574 } 575 576 if !utilfeature.DefaultFeatureGate.Enabled(features.PodLifecycleSleepAction) && !podLifecycleSleepActionInUse(oldPodSpec) { 577 for i := range podSpec.Containers { 578 if podSpec.Containers[i].Lifecycle == nil { 579 continue 580 } 581 if podSpec.Containers[i].Lifecycle.PreStop != nil { 582 podSpec.Containers[i].Lifecycle.PreStop.Sleep = nil 583 } 584 if podSpec.Containers[i].Lifecycle.PostStart != nil { 585 podSpec.Containers[i].Lifecycle.PostStart.Sleep = nil 586 } 587 } 588 for i := range podSpec.InitContainers { 589 if podSpec.InitContainers[i].Lifecycle == nil { 590 continue 591 } 592 if podSpec.InitContainers[i].Lifecycle.PreStop != nil { 593 podSpec.InitContainers[i].Lifecycle.PreStop.Sleep = nil 594 } 595 if podSpec.InitContainers[i].Lifecycle.PostStart != nil { 596 podSpec.InitContainers[i].Lifecycle.PostStart.Sleep = nil 597 } 598 } 599 for i := range podSpec.EphemeralContainers { 600 if podSpec.EphemeralContainers[i].Lifecycle == nil { 601 continue 602 } 603 if podSpec.EphemeralContainers[i].Lifecycle.PreStop != nil { 604 podSpec.EphemeralContainers[i].Lifecycle.PreStop.Sleep = nil 605 } 606 if podSpec.EphemeralContainers[i].Lifecycle.PostStart != nil { 607 podSpec.EphemeralContainers[i].Lifecycle.PostStart.Sleep = nil 608 } 609 } 610 } 611 } 612 613 func podLifecycleSleepActionInUse(podSpec *api.PodSpec) bool { 614 if podSpec == nil { 615 return false 616 } 617 var inUse bool 618 VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool { 619 if c.Lifecycle == nil { 620 return true 621 } 622 if c.Lifecycle.PreStop != nil && c.Lifecycle.PreStop.Sleep != nil { 623 inUse = true 624 return false 625 } 626 if c.Lifecycle.PostStart != nil && c.Lifecycle.PostStart.Sleep != nil { 627 inUse = true 628 return false 629 } 630 return true 631 }) 632 return inUse 633 } 634 635 // dropDisabledPodStatusFields removes disabled fields from the pod status 636 func dropDisabledPodStatusFields(podStatus, oldPodStatus *api.PodStatus, podSpec, oldPodSpec *api.PodSpec) { 637 // the new status is always be non-nil 638 if podStatus == nil { 639 podStatus = &api.PodStatus{} 640 } 641 642 if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && !inPlacePodVerticalScalingInUse(oldPodSpec) { 643 // Drop Resize, AllocatedResources, and Resources fields 644 dropResourcesFields := func(csl []api.ContainerStatus) { 645 for i := range csl { 646 csl[i].AllocatedResources = nil 647 csl[i].Resources = nil 648 } 649 } 650 dropResourcesFields(podStatus.ContainerStatuses) 651 dropResourcesFields(podStatus.InitContainerStatuses) 652 dropResourcesFields(podStatus.EphemeralContainerStatuses) 653 podStatus.Resize = "" 654 } 655 656 if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) && !dynamicResourceAllocationInUse(oldPodSpec) { 657 podStatus.ResourceClaimStatuses = nil 658 } 659 660 // drop HostIPs to empty (disable PodHostIPs). 661 if !utilfeature.DefaultFeatureGate.Enabled(features.PodHostIPs) && !hostIPsInUse(oldPodStatus) { 662 podStatus.HostIPs = nil 663 } 664 } 665 666 func hostIPsInUse(podStatus *api.PodStatus) bool { 667 if podStatus == nil { 668 return false 669 } 670 return len(podStatus.HostIPs) > 0 671 } 672 673 // dropDisabledDynamicResourceAllocationFields removes pod claim references from 674 // container specs and pod-level resource claims unless they are already used 675 // by the old pod spec. 676 func dropDisabledDynamicResourceAllocationFields(podSpec, oldPodSpec *api.PodSpec) { 677 if !utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) && !dynamicResourceAllocationInUse(oldPodSpec) { 678 dropResourceClaimRequests(podSpec.Containers) 679 dropResourceClaimRequests(podSpec.InitContainers) 680 dropEphemeralResourceClaimRequests(podSpec.EphemeralContainers) 681 podSpec.ResourceClaims = nil 682 } 683 } 684 685 func dynamicResourceAllocationInUse(podSpec *api.PodSpec) bool { 686 if podSpec == nil { 687 return false 688 } 689 690 // We only need to check this field because the containers cannot have 691 // resource requirements entries for claims without a corresponding 692 // entry at the pod spec level. 693 return len(podSpec.ResourceClaims) > 0 694 } 695 696 func dropResourceClaimRequests(containers []api.Container) { 697 for i := range containers { 698 containers[i].Resources.Claims = nil 699 } 700 } 701 702 func dropEphemeralResourceClaimRequests(containers []api.EphemeralContainer) { 703 for i := range containers { 704 containers[i].Resources.Claims = nil 705 } 706 } 707 708 // dropDisabledTopologySpreadConstraintsFields removes disabled fields from PodSpec related 709 // to TopologySpreadConstraints only if it is not already used by the old spec. 710 func dropDisabledTopologySpreadConstraintsFields(podSpec, oldPodSpec *api.PodSpec) { 711 if !utilfeature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread) && 712 !minDomainsInUse(oldPodSpec) && 713 podSpec != nil { 714 for i := range podSpec.TopologySpreadConstraints { 715 podSpec.TopologySpreadConstraints[i].MinDomains = nil 716 } 717 } 718 } 719 720 // minDomainsInUse returns true if the pod spec is non-nil 721 // and has non-nil MinDomains field in TopologySpreadConstraints. 722 func minDomainsInUse(podSpec *api.PodSpec) bool { 723 if podSpec == nil { 724 return false 725 } 726 727 for _, c := range podSpec.TopologySpreadConstraints { 728 if c.MinDomains != nil { 729 return true 730 } 731 } 732 return false 733 } 734 735 // dropDisabledProcMountField removes disabled fields from PodSpec related 736 // to ProcMount only if it is not already used by the old spec 737 func dropDisabledProcMountField(podSpec, oldPodSpec *api.PodSpec) { 738 if !utilfeature.DefaultFeatureGate.Enabled(features.ProcMountType) && !procMountInUse(oldPodSpec) { 739 defaultProcMount := api.DefaultProcMount 740 VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool { 741 if c.SecurityContext != nil && c.SecurityContext.ProcMount != nil { 742 // The ProcMount field was improperly forced to non-nil in 1.12. 743 // If the feature is disabled, and the existing object is not using any non-default values, and the ProcMount field is present in the incoming object, force to the default value. 744 // Note: we cannot force the field to nil when the feature is disabled because it causes a diff against previously persisted data. 745 c.SecurityContext.ProcMount = &defaultProcMount 746 } 747 return true 748 }) 749 } 750 } 751 752 // dropDisabledNodeInclusionPolicyFields removes disabled fields from PodSpec related 753 // to NodeInclusionPolicy only if it is not used by the old spec. 754 func dropDisabledNodeInclusionPolicyFields(podSpec, oldPodSpec *api.PodSpec) { 755 if !utilfeature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread) && podSpec != nil { 756 if !nodeTaintsPolicyInUse(oldPodSpec) { 757 for i := range podSpec.TopologySpreadConstraints { 758 podSpec.TopologySpreadConstraints[i].NodeTaintsPolicy = nil 759 } 760 } 761 if !nodeAffinityPolicyInUse(oldPodSpec) { 762 for i := range podSpec.TopologySpreadConstraints { 763 podSpec.TopologySpreadConstraints[i].NodeAffinityPolicy = nil 764 } 765 } 766 } 767 } 768 769 // dropDisabledMatchLabelKeysFieldInPodAffinity removes disabled fields from PodSpec related 770 // to MatchLabelKeys in required/preferred PodAffinity/PodAntiAffinity only if it is not already used by the old spec. 771 func dropDisabledMatchLabelKeysFieldInPodAffinity(podSpec, oldPodSpec *api.PodSpec) { 772 if podSpec == nil || podSpec.Affinity == nil || utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodAffinity) || matchLabelKeysFieldInPodAffinityInUse(oldPodSpec) { 773 return 774 } 775 776 if affinity := podSpec.Affinity.PodAffinity; affinity != nil { 777 dropMatchLabelKeysFieldInPodAffnityTerm(affinity.RequiredDuringSchedulingIgnoredDuringExecution) 778 dropMatchLabelKeysFieldInWeightedPodAffnityTerm(affinity.PreferredDuringSchedulingIgnoredDuringExecution) 779 } 780 if antiaffinity := podSpec.Affinity.PodAntiAffinity; antiaffinity != nil { 781 dropMatchLabelKeysFieldInPodAffnityTerm(antiaffinity.RequiredDuringSchedulingIgnoredDuringExecution) 782 dropMatchLabelKeysFieldInWeightedPodAffnityTerm(antiaffinity.PreferredDuringSchedulingIgnoredDuringExecution) 783 } 784 } 785 786 // dropDisabledMatchLabelKeysFieldInTopologySpread removes disabled fields from PodSpec related 787 // to MatchLabelKeys in TopologySpread only if it is not already used by the old spec. 788 func dropDisabledMatchLabelKeysFieldInTopologySpread(podSpec, oldPodSpec *api.PodSpec) { 789 if !utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread) && !matchLabelKeysInTopologySpreadInUse(oldPodSpec) { 790 for i := range podSpec.TopologySpreadConstraints { 791 podSpec.TopologySpreadConstraints[i].MatchLabelKeys = nil 792 } 793 } 794 } 795 796 // dropMatchLabelKeysFieldInWeightedPodAffnityTerm removes MatchLabelKeys and MismatchLabelKeys fields from WeightedPodAffinityTerm 797 func dropMatchLabelKeysFieldInWeightedPodAffnityTerm(terms []api.WeightedPodAffinityTerm) { 798 for i := range terms { 799 terms[i].PodAffinityTerm.MatchLabelKeys = nil 800 terms[i].PodAffinityTerm.MismatchLabelKeys = nil 801 } 802 } 803 804 // dropMatchLabelKeysFieldInPodAffnityTerm removes MatchLabelKeys and MismatchLabelKeys fields from PodAffinityTerm 805 func dropMatchLabelKeysFieldInPodAffnityTerm(terms []api.PodAffinityTerm) { 806 for i := range terms { 807 terms[i].MatchLabelKeys = nil 808 terms[i].MismatchLabelKeys = nil 809 } 810 } 811 812 // matchLabelKeysFieldInPodAffinityInUse returns true if given affinityTerms have MatchLabelKeys field set. 813 func matchLabelKeysFieldInPodAffinityInUse(podSpec *api.PodSpec) bool { 814 if podSpec == nil || podSpec.Affinity == nil { 815 return false 816 } 817 818 if affinity := podSpec.Affinity.PodAffinity; affinity != nil { 819 for _, c := range affinity.RequiredDuringSchedulingIgnoredDuringExecution { 820 if len(c.MatchLabelKeys) > 0 || len(c.MismatchLabelKeys) > 0 { 821 return true 822 } 823 } 824 825 for _, c := range affinity.PreferredDuringSchedulingIgnoredDuringExecution { 826 if len(c.PodAffinityTerm.MatchLabelKeys) > 0 || len(c.PodAffinityTerm.MismatchLabelKeys) > 0 { 827 return true 828 } 829 } 830 } 831 832 if antiAffinity := podSpec.Affinity.PodAntiAffinity; antiAffinity != nil { 833 for _, c := range antiAffinity.RequiredDuringSchedulingIgnoredDuringExecution { 834 if len(c.MatchLabelKeys) > 0 || len(c.MismatchLabelKeys) > 0 { 835 return true 836 } 837 } 838 839 for _, c := range antiAffinity.PreferredDuringSchedulingIgnoredDuringExecution { 840 if len(c.PodAffinityTerm.MatchLabelKeys) > 0 || len(c.PodAffinityTerm.MismatchLabelKeys) > 0 { 841 return true 842 } 843 } 844 } 845 846 return false 847 } 848 849 // matchLabelKeysInTopologySpreadInUse returns true if the pod spec is non-nil 850 // and has MatchLabelKeys field set in TopologySpreadConstraints. 851 func matchLabelKeysInTopologySpreadInUse(podSpec *api.PodSpec) bool { 852 if podSpec == nil { 853 return false 854 } 855 856 for _, c := range podSpec.TopologySpreadConstraints { 857 if len(c.MatchLabelKeys) > 0 { 858 return true 859 } 860 } 861 return false 862 } 863 864 // nodeAffinityPolicyInUse returns true if the pod spec is non-nil and has NodeAffinityPolicy field set 865 // in TopologySpreadConstraints 866 func nodeAffinityPolicyInUse(podSpec *api.PodSpec) bool { 867 if podSpec == nil { 868 return false 869 } 870 for _, c := range podSpec.TopologySpreadConstraints { 871 if c.NodeAffinityPolicy != nil { 872 return true 873 } 874 } 875 return false 876 } 877 878 // nodeTaintsPolicyInUse returns true if the pod spec is non-nil and has NodeTaintsPolicy field set 879 // in TopologySpreadConstraints 880 func nodeTaintsPolicyInUse(podSpec *api.PodSpec) bool { 881 if podSpec == nil { 882 return false 883 } 884 for _, c := range podSpec.TopologySpreadConstraints { 885 if c.NodeTaintsPolicy != nil { 886 return true 887 } 888 } 889 return false 890 } 891 892 // hostUsersInUse returns true if the pod spec has spec.hostUsers field set. 893 func hostUsersInUse(podSpec *api.PodSpec) bool { 894 if podSpec != nil && podSpec.SecurityContext != nil && podSpec.SecurityContext.HostUsers != nil { 895 return true 896 } 897 898 return false 899 } 900 901 // inPlacePodVerticalScalingInUse returns true if pod spec is non-nil and ResizePolicy is set 902 func inPlacePodVerticalScalingInUse(podSpec *api.PodSpec) bool { 903 if podSpec == nil { 904 return false 905 } 906 var inUse bool 907 VisitContainers(podSpec, Containers, func(c *api.Container, containerType ContainerType) bool { 908 if len(c.ResizePolicy) > 0 { 909 inUse = true 910 return false 911 } 912 return true 913 }) 914 return inUse 915 } 916 917 // procMountInUse returns true if the pod spec is non-nil and has a SecurityContext's ProcMount field set to a non-default value 918 func procMountInUse(podSpec *api.PodSpec) bool { 919 if podSpec == nil { 920 return false 921 } 922 923 var inUse bool 924 VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool { 925 if c.SecurityContext == nil || c.SecurityContext.ProcMount == nil { 926 return true 927 } 928 if *c.SecurityContext.ProcMount != api.DefaultProcMount { 929 inUse = true 930 return false 931 } 932 return true 933 }) 934 935 return inUse 936 } 937 938 // appArmorInUse returns true if the pod has apparmor related information 939 func appArmorInUse(podAnnotations map[string]string) bool { 940 for k := range podAnnotations { 941 if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) { 942 return true 943 } 944 } 945 return false 946 } 947 948 // schedulingGatesInUse returns true if the pod spec is non-nil and it has SchedulingGates field set. 949 func schedulingGatesInUse(podSpec *api.PodSpec) bool { 950 if podSpec == nil { 951 return false 952 } 953 return len(podSpec.SchedulingGates) != 0 954 } 955 956 // restartableInitContainersInUse returns true if the pod spec is non-nil and 957 // it has any init container with ContainerRestartPolicyAlways. 958 func restartableInitContainersInUse(podSpec *api.PodSpec) bool { 959 if podSpec == nil { 960 return false 961 } 962 var inUse bool 963 VisitContainers(podSpec, InitContainers, func(c *api.Container, containerType ContainerType) bool { 964 if c.RestartPolicy != nil && *c.RestartPolicy == api.ContainerRestartPolicyAlways { 965 inUse = true 966 return false 967 } 968 return true 969 }) 970 return inUse 971 } 972 973 func clusterTrustBundleProjectionInUse(podSpec *api.PodSpec) bool { 974 if podSpec == nil { 975 return false 976 } 977 for _, v := range podSpec.Volumes { 978 if v.Projected == nil { 979 continue 980 } 981 982 for _, s := range v.Projected.Sources { 983 if s.ClusterTrustBundle != nil { 984 return true 985 } 986 } 987 } 988 989 return false 990 } 991 992 func dropDisabledClusterTrustBundleProjection(podSpec, oldPodSpec *api.PodSpec) { 993 if utilfeature.DefaultFeatureGate.Enabled(features.ClusterTrustBundleProjection) { 994 return 995 } 996 if podSpec == nil { 997 return 998 } 999 1000 // If the pod was already using it, it can keep using it. 1001 if clusterTrustBundleProjectionInUse(oldPodSpec) { 1002 return 1003 } 1004 1005 for i := range podSpec.Volumes { 1006 if podSpec.Volumes[i].Projected == nil { 1007 continue 1008 } 1009 1010 for j := range podSpec.Volumes[i].Projected.Sources { 1011 podSpec.Volumes[i].Projected.Sources[j].ClusterTrustBundle = nil 1012 } 1013 } 1014 } 1015 1016 func hasInvalidLabelValueInAffinitySelector(spec *api.PodSpec) bool { 1017 if spec.Affinity != nil { 1018 if spec.Affinity.PodAffinity != nil { 1019 for _, term := range spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution { 1020 allErrs := apivalidation.ValidatePodAffinityTermSelector(term, false, nil) 1021 if len(allErrs) != 0 { 1022 return true 1023 } 1024 } 1025 for _, term := range spec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution { 1026 allErrs := apivalidation.ValidatePodAffinityTermSelector(term.PodAffinityTerm, false, nil) 1027 if len(allErrs) != 0 { 1028 return true 1029 } 1030 } 1031 } 1032 if spec.Affinity.PodAntiAffinity != nil { 1033 for _, term := range spec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution { 1034 allErrs := apivalidation.ValidatePodAffinityTermSelector(term, false, nil) 1035 if len(allErrs) != 0 { 1036 return true 1037 } 1038 } 1039 for _, term := range spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution { 1040 allErrs := apivalidation.ValidatePodAffinityTermSelector(term.PodAffinityTerm, false, nil) 1041 if len(allErrs) != 0 { 1042 return true 1043 } 1044 } 1045 } 1046 } 1047 return false 1048 } 1049 1050 func MarkPodProposedForResize(oldPod, newPod *api.Pod) { 1051 for i, c := range newPod.Spec.Containers { 1052 if c.Resources.Requests == nil { 1053 continue 1054 } 1055 if cmp.Equal(oldPod.Spec.Containers[i].Resources, c.Resources) { 1056 continue 1057 } 1058 findContainerStatus := func(css []api.ContainerStatus, cName string) (api.ContainerStatus, bool) { 1059 for i := range css { 1060 if css[i].Name == cName { 1061 return css[i], true 1062 } 1063 } 1064 return api.ContainerStatus{}, false 1065 } 1066 if cs, ok := findContainerStatus(newPod.Status.ContainerStatuses, c.Name); ok { 1067 if !cmp.Equal(c.Resources.Requests, cs.AllocatedResources) { 1068 newPod.Status.Resize = api.PodResizeStatusProposed 1069 break 1070 } 1071 } 1072 } 1073 }