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  }