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