k8s.io/kubernetes@v1.29.3/pkg/apis/core/validation/validation.go (about)

     1  /*
     2  Copyright 2014 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 validation
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"math"
    23  	"net"
    24  	"path"
    25  	"path/filepath"
    26  	"reflect"
    27  	"regexp"
    28  	"strings"
    29  	"sync"
    30  	"unicode"
    31  	"unicode/utf8"
    32  
    33  	"github.com/google/go-cmp/cmp"
    34  	v1 "k8s.io/api/core/v1"
    35  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    36  	"k8s.io/apimachinery/pkg/api/resource"
    37  	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
    38  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    39  	unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
    40  	"k8s.io/apimachinery/pkg/conversion"
    41  	"k8s.io/apimachinery/pkg/labels"
    42  	"k8s.io/apimachinery/pkg/util/intstr"
    43  	"k8s.io/apimachinery/pkg/util/sets"
    44  	"k8s.io/apimachinery/pkg/util/validation"
    45  	"k8s.io/apimachinery/pkg/util/validation/field"
    46  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    47  	utilsysctl "k8s.io/component-helpers/node/util/sysctl"
    48  	schedulinghelper "k8s.io/component-helpers/scheduling/corev1"
    49  	kubeletapis "k8s.io/kubelet/pkg/apis"
    50  	apiservice "k8s.io/kubernetes/pkg/api/service"
    51  	"k8s.io/kubernetes/pkg/apis/core"
    52  	"k8s.io/kubernetes/pkg/apis/core/helper"
    53  	"k8s.io/kubernetes/pkg/apis/core/helper/qos"
    54  	podshelper "k8s.io/kubernetes/pkg/apis/core/pods"
    55  	corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
    56  	"k8s.io/kubernetes/pkg/capabilities"
    57  	"k8s.io/kubernetes/pkg/cluster/ports"
    58  	"k8s.io/kubernetes/pkg/features"
    59  	"k8s.io/kubernetes/pkg/fieldpath"
    60  	netutils "k8s.io/utils/net"
    61  )
    62  
    63  const isNegativeErrorMsg string = apimachineryvalidation.IsNegativeErrorMsg
    64  const isInvalidQuotaResource string = `must be a standard resource for quota`
    65  const fieldImmutableErrorMsg string = apimachineryvalidation.FieldImmutableErrorMsg
    66  const isNotIntegerErrorMsg string = `must be an integer`
    67  const isNotPositiveErrorMsg string = `must be greater than zero`
    68  
    69  var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
    70  var fileModeErrorMsg = "must be a number between 0 and 0777 (octal), both inclusive"
    71  
    72  // BannedOwners is a black list of object that are not allowed to be owners.
    73  var BannedOwners = apimachineryvalidation.BannedOwners
    74  
    75  var iscsiInitiatorIqnRegex = regexp.MustCompile(`iqn\.\d{4}-\d{2}\.([[:alnum:]-.]+)(:[^,;*&$|\s]+)$`)
    76  var iscsiInitiatorEuiRegex = regexp.MustCompile(`^eui.[[:alnum:]]{16}$`)
    77  var iscsiInitiatorNaaRegex = regexp.MustCompile(`^naa.[[:alnum:]]{32}$`)
    78  
    79  var allowedEphemeralContainerFields = map[string]bool{
    80  	"Name":                     true,
    81  	"Image":                    true,
    82  	"Command":                  true,
    83  	"Args":                     true,
    84  	"WorkingDir":               true,
    85  	"Ports":                    false,
    86  	"EnvFrom":                  true,
    87  	"Env":                      true,
    88  	"Resources":                false,
    89  	"VolumeMounts":             true,
    90  	"VolumeDevices":            true,
    91  	"LivenessProbe":            false,
    92  	"ReadinessProbe":           false,
    93  	"StartupProbe":             false,
    94  	"Lifecycle":                false,
    95  	"TerminationMessagePath":   true,
    96  	"TerminationMessagePolicy": true,
    97  	"ImagePullPolicy":          true,
    98  	"SecurityContext":          true,
    99  	"Stdin":                    true,
   100  	"StdinOnce":                true,
   101  	"TTY":                      true,
   102  }
   103  
   104  // validOS stores the set of valid OSes within pod spec.
   105  // The valid values currently are linux, windows.
   106  // In future, they can be expanded to values from
   107  // https://github.com/opencontainers/runtime-spec/blob/master/config.md#platform-specific-configuration
   108  var validOS = sets.New(core.Linux, core.Windows)
   109  
   110  // ValidateHasLabel requires that metav1.ObjectMeta has a Label with key and expectedValue
   111  func ValidateHasLabel(meta metav1.ObjectMeta, fldPath *field.Path, key, expectedValue string) field.ErrorList {
   112  	allErrs := field.ErrorList{}
   113  	actualValue, found := meta.Labels[key]
   114  	if !found {
   115  		allErrs = append(allErrs, field.Required(fldPath.Child("labels").Key(key),
   116  			fmt.Sprintf("must be '%s'", expectedValue)))
   117  		return allErrs
   118  	}
   119  	if actualValue != expectedValue {
   120  		allErrs = append(allErrs, field.Invalid(fldPath.Child("labels").Key(key), meta.Labels,
   121  			fmt.Sprintf("must be '%s'", expectedValue)))
   122  	}
   123  	return allErrs
   124  }
   125  
   126  // ValidateAnnotations validates that a set of annotations are correctly defined.
   127  func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
   128  	return apimachineryvalidation.ValidateAnnotations(annotations, fldPath)
   129  }
   130  
   131  func ValidateDNS1123Label(value string, fldPath *field.Path) field.ErrorList {
   132  	allErrs := field.ErrorList{}
   133  	for _, msg := range validation.IsDNS1123Label(value) {
   134  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
   135  	}
   136  	return allErrs
   137  }
   138  
   139  // ValidateQualifiedName validates if name is what Kubernetes calls a "qualified name".
   140  func ValidateQualifiedName(value string, fldPath *field.Path) field.ErrorList {
   141  	allErrs := field.ErrorList{}
   142  	for _, msg := range validation.IsQualifiedName(value) {
   143  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
   144  	}
   145  	return allErrs
   146  }
   147  
   148  // ValidateDNS1123Subdomain validates that a name is a proper DNS subdomain.
   149  func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList {
   150  	allErrs := field.ErrorList{}
   151  	for _, msg := range validation.IsDNS1123Subdomain(value) {
   152  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
   153  	}
   154  	return allErrs
   155  }
   156  
   157  func ValidatePodSpecificAnnotations(annotations map[string]string, spec *core.PodSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
   158  	allErrs := field.ErrorList{}
   159  
   160  	if value, isMirror := annotations[core.MirrorPodAnnotationKey]; isMirror {
   161  		if len(spec.NodeName) == 0 {
   162  			allErrs = append(allErrs, field.Invalid(fldPath.Key(core.MirrorPodAnnotationKey), value, "must set spec.nodeName if mirror pod annotation is set"))
   163  		}
   164  	}
   165  
   166  	if annotations[core.TolerationsAnnotationKey] != "" {
   167  		allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath)...)
   168  	}
   169  
   170  	if !opts.AllowInvalidPodDeletionCost {
   171  		if _, err := helper.GetDeletionCostFromPodAnnotations(annotations); err != nil {
   172  			allErrs = append(allErrs, field.Invalid(fldPath.Key(core.PodDeletionCost), annotations[core.PodDeletionCost], "must be a 32bit integer"))
   173  		}
   174  	}
   175  
   176  	allErrs = append(allErrs, ValidateSeccompPodAnnotations(annotations, fldPath)...)
   177  	allErrs = append(allErrs, ValidateAppArmorPodAnnotations(annotations, spec, fldPath)...)
   178  
   179  	return allErrs
   180  }
   181  
   182  // ValidateTolerationsInPodAnnotations tests that the serialized tolerations in Pod.Annotations has valid data
   183  func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
   184  	allErrs := field.ErrorList{}
   185  
   186  	tolerations, err := helper.GetTolerationsFromPodAnnotations(annotations)
   187  	if err != nil {
   188  		allErrs = append(allErrs, field.Invalid(fldPath, core.TolerationsAnnotationKey, err.Error()))
   189  		return allErrs
   190  	}
   191  
   192  	if len(tolerations) > 0 {
   193  		allErrs = append(allErrs, ValidateTolerations(tolerations, fldPath.Child(core.TolerationsAnnotationKey))...)
   194  	}
   195  
   196  	return allErrs
   197  }
   198  
   199  func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *core.Pod, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
   200  	allErrs := field.ErrorList{}
   201  	newAnnotations := newPod.Annotations
   202  	oldAnnotations := oldPod.Annotations
   203  	for k, oldVal := range oldAnnotations {
   204  		if newVal, exists := newAnnotations[k]; exists && newVal == oldVal {
   205  			continue // No change.
   206  		}
   207  		if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) {
   208  			allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove or update AppArmor annotations"))
   209  		}
   210  		if k == core.MirrorPodAnnotationKey {
   211  			allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not remove or update mirror pod annotation"))
   212  		}
   213  	}
   214  	// Check for additions
   215  	for k := range newAnnotations {
   216  		if _, ok := oldAnnotations[k]; ok {
   217  			continue // No change.
   218  		}
   219  		if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) {
   220  			allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not add AppArmor annotations"))
   221  		}
   222  		if k == core.MirrorPodAnnotationKey {
   223  			allErrs = append(allErrs, field.Forbidden(fldPath.Key(k), "may not add mirror pod annotation"))
   224  		}
   225  	}
   226  	allErrs = append(allErrs, ValidatePodSpecificAnnotations(newAnnotations, &newPod.Spec, fldPath, opts)...)
   227  	return allErrs
   228  }
   229  
   230  func ValidateEndpointsSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
   231  	allErrs := field.ErrorList{}
   232  	return allErrs
   233  }
   234  
   235  // ValidateNameFunc validates that the provided name is valid for a given resource type.
   236  // Not all resources have the same validation rules for names. Prefix is true
   237  // if the name will have a value appended to it.  If the name is not valid,
   238  // this returns a list of descriptions of individual characteristics of the
   239  // value that were not valid.  Otherwise this returns an empty list or nil.
   240  type ValidateNameFunc apimachineryvalidation.ValidateNameFunc
   241  
   242  // ValidatePodName can be used to check whether the given pod name is valid.
   243  // Prefix indicates this name will be used as part of generation, in which case
   244  // trailing dashes are allowed.
   245  var ValidatePodName = apimachineryvalidation.NameIsDNSSubdomain
   246  
   247  // ValidateReplicationControllerName can be used to check whether the given replication
   248  // controller name is valid.
   249  // Prefix indicates this name will be used as part of generation, in which case
   250  // trailing dashes are allowed.
   251  var ValidateReplicationControllerName = apimachineryvalidation.NameIsDNSSubdomain
   252  
   253  // ValidateServiceName can be used to check whether the given service name is valid.
   254  // Prefix indicates this name will be used as part of generation, in which case
   255  // trailing dashes are allowed.
   256  var ValidateServiceName = apimachineryvalidation.NameIsDNS1035Label
   257  
   258  // ValidateNodeName can be used to check whether the given node name is valid.
   259  // Prefix indicates this name will be used as part of generation, in which case
   260  // trailing dashes are allowed.
   261  var ValidateNodeName = apimachineryvalidation.NameIsDNSSubdomain
   262  
   263  // ValidateNamespaceName can be used to check whether the given namespace name is valid.
   264  // Prefix indicates this name will be used as part of generation, in which case
   265  // trailing dashes are allowed.
   266  var ValidateNamespaceName = apimachineryvalidation.ValidateNamespaceName
   267  
   268  // ValidateLimitRangeName can be used to check whether the given limit range name is valid.
   269  // Prefix indicates this name will be used as part of generation, in which case
   270  // trailing dashes are allowed.
   271  var ValidateLimitRangeName = apimachineryvalidation.NameIsDNSSubdomain
   272  
   273  // ValidateResourceQuotaName can be used to check whether the given
   274  // resource quota name is valid.
   275  // Prefix indicates this name will be used as part of generation, in which case
   276  // trailing dashes are allowed.
   277  var ValidateResourceQuotaName = apimachineryvalidation.NameIsDNSSubdomain
   278  
   279  // ValidateSecretName can be used to check whether the given secret name is valid.
   280  // Prefix indicates this name will be used as part of generation, in which case
   281  // trailing dashes are allowed.
   282  var ValidateSecretName = apimachineryvalidation.NameIsDNSSubdomain
   283  
   284  // ValidateServiceAccountName can be used to check whether the given service account name is valid.
   285  // Prefix indicates this name will be used as part of generation, in which case
   286  // trailing dashes are allowed.
   287  var ValidateServiceAccountName = apimachineryvalidation.ValidateServiceAccountName
   288  
   289  // ValidateEndpointsName can be used to check whether the given endpoints name is valid.
   290  // Prefix indicates this name will be used as part of generation, in which case
   291  // trailing dashes are allowed.
   292  var ValidateEndpointsName = apimachineryvalidation.NameIsDNSSubdomain
   293  
   294  // ValidateClassName can be used to check whether the given class name is valid.
   295  // It is defined here to avoid import cycle between pkg/apis/storage/validation
   296  // (where it should be) and this file.
   297  var ValidateClassName = apimachineryvalidation.NameIsDNSSubdomain
   298  
   299  // ValidatePriorityClassName can be used to check whether the given priority
   300  // class name is valid.
   301  var ValidatePriorityClassName = apimachineryvalidation.NameIsDNSSubdomain
   302  
   303  // ValidateResourceClaimName can be used to check whether the given
   304  // name for a ResourceClaim is valid.
   305  var ValidateResourceClaimName = apimachineryvalidation.NameIsDNSSubdomain
   306  
   307  // ValidateResourceClaimTemplateName can be used to check whether the given
   308  // name for a ResourceClaimTemplate is valid.
   309  var ValidateResourceClaimTemplateName = apimachineryvalidation.NameIsDNSSubdomain
   310  
   311  // ValidateRuntimeClassName can be used to check whether the given RuntimeClass name is valid.
   312  // Prefix indicates this name will be used as part of generation, in which case
   313  // trailing dashes are allowed.
   314  func ValidateRuntimeClassName(name string, fldPath *field.Path) field.ErrorList {
   315  	var allErrs field.ErrorList
   316  	for _, msg := range apimachineryvalidation.NameIsDNSSubdomain(name, false) {
   317  		allErrs = append(allErrs, field.Invalid(fldPath, name, msg))
   318  	}
   319  	return allErrs
   320  }
   321  
   322  // validateOverhead can be used to check whether the given Overhead is valid.
   323  func validateOverhead(overhead core.ResourceList, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
   324  	// reuse the ResourceRequirements validation logic
   325  	return ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead}, nil, fldPath, opts)
   326  }
   327  
   328  // Validates that given value is not negative.
   329  func ValidateNonnegativeField(value int64, fldPath *field.Path) field.ErrorList {
   330  	return apimachineryvalidation.ValidateNonnegativeField(value, fldPath)
   331  }
   332  
   333  // Validates that a Quantity is not negative
   334  func ValidateNonnegativeQuantity(value resource.Quantity, fldPath *field.Path) field.ErrorList {
   335  	allErrs := field.ErrorList{}
   336  	if value.Cmp(resource.Quantity{}) < 0 {
   337  		allErrs = append(allErrs, field.Invalid(fldPath, value.String(), isNegativeErrorMsg))
   338  	}
   339  	return allErrs
   340  }
   341  
   342  // Validates that a Quantity is positive
   343  func ValidatePositiveQuantityValue(value resource.Quantity, fldPath *field.Path) field.ErrorList {
   344  	allErrs := field.ErrorList{}
   345  	if value.Cmp(resource.Quantity{}) <= 0 {
   346  		allErrs = append(allErrs, field.Invalid(fldPath, value.String(), isNotPositiveErrorMsg))
   347  	}
   348  	return allErrs
   349  }
   350  
   351  func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) field.ErrorList {
   352  	return apimachineryvalidation.ValidateImmutableField(newVal, oldVal, fldPath)
   353  }
   354  
   355  func ValidateImmutableAnnotation(newVal string, oldVal string, annotation string, fldPath *field.Path) field.ErrorList {
   356  	allErrs := field.ErrorList{}
   357  
   358  	if oldVal != newVal {
   359  		allErrs = append(allErrs, field.Invalid(fldPath.Child("annotations", annotation), newVal, fieldImmutableErrorMsg))
   360  	}
   361  	return allErrs
   362  }
   363  
   364  // ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
   365  // been performed.
   366  // It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
   367  // TODO: Remove calls to this method scattered in validations of specific resources, e.g., ValidatePodUpdate.
   368  func ValidateObjectMeta(meta *metav1.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
   369  	allErrs := apimachineryvalidation.ValidateObjectMeta(meta, requiresNamespace, apimachineryvalidation.ValidateNameFunc(nameFn), fldPath)
   370  	// run additional checks for the finalizer name
   371  	for i := range meta.Finalizers {
   372  		allErrs = append(allErrs, validateKubeFinalizerName(string(meta.Finalizers[i]), fldPath.Child("finalizers").Index(i))...)
   373  	}
   374  	return allErrs
   375  }
   376  
   377  // ValidateObjectMetaUpdate validates an object's metadata when updated
   378  func ValidateObjectMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
   379  	allErrs := apimachineryvalidation.ValidateObjectMetaUpdate(newMeta, oldMeta, fldPath)
   380  	// run additional checks for the finalizer name
   381  	for i := range newMeta.Finalizers {
   382  		allErrs = append(allErrs, validateKubeFinalizerName(string(newMeta.Finalizers[i]), fldPath.Child("finalizers").Index(i))...)
   383  	}
   384  
   385  	return allErrs
   386  }
   387  
   388  func ValidateVolumes(volumes []core.Volume, podMeta *metav1.ObjectMeta, fldPath *field.Path, opts PodValidationOptions) (map[string]core.VolumeSource, field.ErrorList) {
   389  	allErrs := field.ErrorList{}
   390  
   391  	allNames := sets.Set[string]{}
   392  	allCreatedPVCs := sets.Set[string]{}
   393  	// Determine which PVCs will be created for this pod. We need
   394  	// the exact name of the pod for this. Without it, this sanity
   395  	// check has to be skipped.
   396  	if podMeta != nil && podMeta.Name != "" {
   397  		for _, vol := range volumes {
   398  			if vol.VolumeSource.Ephemeral != nil {
   399  				allCreatedPVCs.Insert(podMeta.Name + "-" + vol.Name)
   400  			}
   401  		}
   402  	}
   403  	vols := make(map[string]core.VolumeSource)
   404  	for i, vol := range volumes {
   405  		idxPath := fldPath.Index(i)
   406  		namePath := idxPath.Child("name")
   407  		el := validateVolumeSource(&vol.VolumeSource, idxPath, vol.Name, podMeta, opts)
   408  		if len(vol.Name) == 0 {
   409  			el = append(el, field.Required(namePath, ""))
   410  		} else {
   411  			el = append(el, ValidateDNS1123Label(vol.Name, namePath)...)
   412  		}
   413  		if allNames.Has(vol.Name) {
   414  			el = append(el, field.Duplicate(namePath, vol.Name))
   415  		}
   416  		if len(el) == 0 {
   417  			allNames.Insert(vol.Name)
   418  			vols[vol.Name] = vol.VolumeSource
   419  		} else {
   420  			allErrs = append(allErrs, el...)
   421  		}
   422  		// A PersistentVolumeClaimSource should not reference a created PVC. That doesn't
   423  		// make sense.
   424  		if vol.PersistentVolumeClaim != nil && allCreatedPVCs.Has(vol.PersistentVolumeClaim.ClaimName) {
   425  			allErrs = append(allErrs, field.Invalid(idxPath.Child("persistentVolumeClaim").Child("claimName"), vol.PersistentVolumeClaim.ClaimName,
   426  				"must not reference a PVC that gets created for an ephemeral volume"))
   427  		}
   428  	}
   429  
   430  	return vols, allErrs
   431  }
   432  
   433  func IsMatchedVolume(name string, volumes map[string]core.VolumeSource) bool {
   434  	if _, ok := volumes[name]; ok {
   435  		return true
   436  	}
   437  	return false
   438  }
   439  
   440  // isMatched checks whether the volume with the given name is used by a
   441  // container and if so, if it involves a PVC.
   442  func isMatchedDevice(name string, volumes map[string]core.VolumeSource) (isMatched bool, isPVC bool) {
   443  	if source, ok := volumes[name]; ok {
   444  		if source.PersistentVolumeClaim != nil ||
   445  			source.Ephemeral != nil {
   446  			return true, true
   447  		}
   448  		return true, false
   449  	}
   450  	return false, false
   451  }
   452  
   453  func mountNameAlreadyExists(name string, devices map[string]string) bool {
   454  	if _, ok := devices[name]; ok {
   455  		return true
   456  	}
   457  	return false
   458  }
   459  
   460  func mountPathAlreadyExists(mountPath string, devices map[string]string) bool {
   461  	for _, devPath := range devices {
   462  		if mountPath == devPath {
   463  			return true
   464  		}
   465  	}
   466  
   467  	return false
   468  }
   469  
   470  func deviceNameAlreadyExists(name string, mounts map[string]string) bool {
   471  	if _, ok := mounts[name]; ok {
   472  		return true
   473  	}
   474  	return false
   475  }
   476  
   477  func devicePathAlreadyExists(devicePath string, mounts map[string]string) bool {
   478  	for _, mountPath := range mounts {
   479  		if mountPath == devicePath {
   480  			return true
   481  		}
   482  	}
   483  
   484  	return false
   485  }
   486  
   487  func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volName string, podMeta *metav1.ObjectMeta, opts PodValidationOptions) field.ErrorList {
   488  	numVolumes := 0
   489  	allErrs := field.ErrorList{}
   490  	if source.EmptyDir != nil {
   491  		numVolumes++
   492  		if source.EmptyDir.SizeLimit != nil && source.EmptyDir.SizeLimit.Cmp(resource.Quantity{}) < 0 {
   493  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("emptyDir").Child("sizeLimit"), "SizeLimit field must be a valid resource quantity"))
   494  		}
   495  	}
   496  	if source.HostPath != nil {
   497  		if numVolumes > 0 {
   498  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostPath"), "may not specify more than 1 volume type"))
   499  		} else {
   500  			numVolumes++
   501  			allErrs = append(allErrs, validateHostPathVolumeSource(source.HostPath, fldPath.Child("hostPath"))...)
   502  		}
   503  	}
   504  	if source.GitRepo != nil {
   505  		if numVolumes > 0 {
   506  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("gitRepo"), "may not specify more than 1 volume type"))
   507  		} else {
   508  			numVolumes++
   509  			allErrs = append(allErrs, validateGitRepoVolumeSource(source.GitRepo, fldPath.Child("gitRepo"))...)
   510  		}
   511  	}
   512  	if source.GCEPersistentDisk != nil {
   513  		if numVolumes > 0 {
   514  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("gcePersistentDisk"), "may not specify more than 1 volume type"))
   515  		} else {
   516  			numVolumes++
   517  			allErrs = append(allErrs, validateGCEPersistentDiskVolumeSource(source.GCEPersistentDisk, fldPath.Child("persistentDisk"))...)
   518  		}
   519  	}
   520  	if source.AWSElasticBlockStore != nil {
   521  		if numVolumes > 0 {
   522  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("awsElasticBlockStore"), "may not specify more than 1 volume type"))
   523  		} else {
   524  			numVolumes++
   525  			allErrs = append(allErrs, validateAWSElasticBlockStoreVolumeSource(source.AWSElasticBlockStore, fldPath.Child("awsElasticBlockStore"))...)
   526  		}
   527  	}
   528  	if source.Secret != nil {
   529  		if numVolumes > 0 {
   530  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("secret"), "may not specify more than 1 volume type"))
   531  		} else {
   532  			numVolumes++
   533  			allErrs = append(allErrs, validateSecretVolumeSource(source.Secret, fldPath.Child("secret"))...)
   534  		}
   535  	}
   536  	if source.NFS != nil {
   537  		if numVolumes > 0 {
   538  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("nfs"), "may not specify more than 1 volume type"))
   539  		} else {
   540  			numVolumes++
   541  			allErrs = append(allErrs, validateNFSVolumeSource(source.NFS, fldPath.Child("nfs"))...)
   542  		}
   543  	}
   544  	if source.ISCSI != nil {
   545  		if numVolumes > 0 {
   546  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("iscsi"), "may not specify more than 1 volume type"))
   547  		} else {
   548  			numVolumes++
   549  			allErrs = append(allErrs, validateISCSIVolumeSource(source.ISCSI, fldPath.Child("iscsi"))...)
   550  		}
   551  		if source.ISCSI.InitiatorName != nil && len(volName+":"+source.ISCSI.TargetPortal) > 64 {
   552  			tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
   553  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), volName, tooLongErr))
   554  		}
   555  	}
   556  	if source.Glusterfs != nil {
   557  		if numVolumes > 0 {
   558  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("glusterfs"), "may not specify more than 1 volume type"))
   559  		} else {
   560  			numVolumes++
   561  			allErrs = append(allErrs, validateGlusterfsVolumeSource(source.Glusterfs, fldPath.Child("glusterfs"))...)
   562  		}
   563  	}
   564  	if source.Flocker != nil {
   565  		if numVolumes > 0 {
   566  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("flocker"), "may not specify more than 1 volume type"))
   567  		} else {
   568  			numVolumes++
   569  			allErrs = append(allErrs, validateFlockerVolumeSource(source.Flocker, fldPath.Child("flocker"))...)
   570  		}
   571  	}
   572  	if source.PersistentVolumeClaim != nil {
   573  		if numVolumes > 0 {
   574  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("persistentVolumeClaim"), "may not specify more than 1 volume type"))
   575  		} else {
   576  			numVolumes++
   577  			allErrs = append(allErrs, validatePersistentClaimVolumeSource(source.PersistentVolumeClaim, fldPath.Child("persistentVolumeClaim"))...)
   578  		}
   579  	}
   580  	if source.RBD != nil {
   581  		if numVolumes > 0 {
   582  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("rbd"), "may not specify more than 1 volume type"))
   583  		} else {
   584  			numVolumes++
   585  			allErrs = append(allErrs, validateRBDVolumeSource(source.RBD, fldPath.Child("rbd"))...)
   586  		}
   587  	}
   588  	if source.Cinder != nil {
   589  		if numVolumes > 0 {
   590  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("cinder"), "may not specify more than 1 volume type"))
   591  		} else {
   592  			numVolumes++
   593  			allErrs = append(allErrs, validateCinderVolumeSource(source.Cinder, fldPath.Child("cinder"))...)
   594  		}
   595  	}
   596  	if source.CephFS != nil {
   597  		if numVolumes > 0 {
   598  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("cephFS"), "may not specify more than 1 volume type"))
   599  		} else {
   600  			numVolumes++
   601  			allErrs = append(allErrs, validateCephFSVolumeSource(source.CephFS, fldPath.Child("cephfs"))...)
   602  		}
   603  	}
   604  	if source.Quobyte != nil {
   605  		if numVolumes > 0 {
   606  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("quobyte"), "may not specify more than 1 volume type"))
   607  		} else {
   608  			numVolumes++
   609  			allErrs = append(allErrs, validateQuobyteVolumeSource(source.Quobyte, fldPath.Child("quobyte"))...)
   610  		}
   611  	}
   612  	if source.DownwardAPI != nil {
   613  		if numVolumes > 0 {
   614  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("downwarAPI"), "may not specify more than 1 volume type"))
   615  		} else {
   616  			numVolumes++
   617  			allErrs = append(allErrs, validateDownwardAPIVolumeSource(source.DownwardAPI, fldPath.Child("downwardAPI"), opts)...)
   618  		}
   619  	}
   620  	if source.FC != nil {
   621  		if numVolumes > 0 {
   622  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("fc"), "may not specify more than 1 volume type"))
   623  		} else {
   624  			numVolumes++
   625  			allErrs = append(allErrs, validateFCVolumeSource(source.FC, fldPath.Child("fc"))...)
   626  		}
   627  	}
   628  	if source.FlexVolume != nil {
   629  		if numVolumes > 0 {
   630  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("flexVolume"), "may not specify more than 1 volume type"))
   631  		} else {
   632  			numVolumes++
   633  			allErrs = append(allErrs, validateFlexVolumeSource(source.FlexVolume, fldPath.Child("flexVolume"))...)
   634  		}
   635  	}
   636  	if source.ConfigMap != nil {
   637  		if numVolumes > 0 {
   638  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("configMap"), "may not specify more than 1 volume type"))
   639  		} else {
   640  			numVolumes++
   641  			allErrs = append(allErrs, validateConfigMapVolumeSource(source.ConfigMap, fldPath.Child("configMap"))...)
   642  		}
   643  	}
   644  
   645  	if source.AzureFile != nil {
   646  		if numVolumes > 0 {
   647  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureFile"), "may not specify more than 1 volume type"))
   648  		} else {
   649  			numVolumes++
   650  			allErrs = append(allErrs, validateAzureFile(source.AzureFile, fldPath.Child("azureFile"))...)
   651  		}
   652  	}
   653  
   654  	if source.VsphereVolume != nil {
   655  		if numVolumes > 0 {
   656  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("vsphereVolume"), "may not specify more than 1 volume type"))
   657  		} else {
   658  			numVolumes++
   659  			allErrs = append(allErrs, validateVsphereVolumeSource(source.VsphereVolume, fldPath.Child("vsphereVolume"))...)
   660  		}
   661  	}
   662  	if source.PhotonPersistentDisk != nil {
   663  		if numVolumes > 0 {
   664  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("photonPersistentDisk"), "may not specify more than 1 volume type"))
   665  		} else {
   666  			numVolumes++
   667  			allErrs = append(allErrs, validatePhotonPersistentDiskVolumeSource(source.PhotonPersistentDisk, fldPath.Child("photonPersistentDisk"))...)
   668  		}
   669  	}
   670  	if source.PortworxVolume != nil {
   671  		if numVolumes > 0 {
   672  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("portworxVolume"), "may not specify more than 1 volume type"))
   673  		} else {
   674  			numVolumes++
   675  			allErrs = append(allErrs, validatePortworxVolumeSource(source.PortworxVolume, fldPath.Child("portworxVolume"))...)
   676  		}
   677  	}
   678  	if source.AzureDisk != nil {
   679  		if numVolumes > 0 {
   680  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureDisk"), "may not specify more than 1 volume type"))
   681  		} else {
   682  			numVolumes++
   683  			allErrs = append(allErrs, validateAzureDisk(source.AzureDisk, fldPath.Child("azureDisk"))...)
   684  		}
   685  	}
   686  	if source.StorageOS != nil {
   687  		if numVolumes > 0 {
   688  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("storageos"), "may not specify more than 1 volume type"))
   689  		} else {
   690  			numVolumes++
   691  			allErrs = append(allErrs, validateStorageOSVolumeSource(source.StorageOS, fldPath.Child("storageos"))...)
   692  		}
   693  	}
   694  	if source.Projected != nil {
   695  		if numVolumes > 0 {
   696  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("projected"), "may not specify more than 1 volume type"))
   697  		} else {
   698  			numVolumes++
   699  			allErrs = append(allErrs, validateProjectedVolumeSource(source.Projected, fldPath.Child("projected"), opts)...)
   700  		}
   701  	}
   702  	if source.ScaleIO != nil {
   703  		if numVolumes > 0 {
   704  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("scaleIO"), "may not specify more than 1 volume type"))
   705  		} else {
   706  			numVolumes++
   707  			allErrs = append(allErrs, validateScaleIOVolumeSource(source.ScaleIO, fldPath.Child("scaleIO"))...)
   708  		}
   709  	}
   710  	if source.CSI != nil {
   711  		if numVolumes > 0 {
   712  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("csi"), "may not specify more than 1 volume type"))
   713  		} else {
   714  			numVolumes++
   715  			allErrs = append(allErrs, validateCSIVolumeSource(source.CSI, fldPath.Child("csi"))...)
   716  		}
   717  	}
   718  	if source.Ephemeral != nil {
   719  		if numVolumes > 0 {
   720  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("ephemeral"), "may not specify more than 1 volume type"))
   721  		} else {
   722  			numVolumes++
   723  			allErrs = append(allErrs, validateEphemeralVolumeSource(source.Ephemeral, fldPath.Child("ephemeral"))...)
   724  			// Check the expected name for the PVC. This gets skipped if information is missing,
   725  			// because that already gets flagged as a problem elsewhere. For example,
   726  			// ValidateObjectMeta as called by validatePodMetadataAndSpec checks that the name is set.
   727  			if podMeta != nil && podMeta.Name != "" && volName != "" {
   728  				pvcName := podMeta.Name + "-" + volName
   729  				for _, msg := range ValidatePersistentVolumeName(pvcName, false) {
   730  					allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), volName, fmt.Sprintf("PVC name %q: %v", pvcName, msg)))
   731  				}
   732  			}
   733  		}
   734  	}
   735  
   736  	if numVolumes == 0 {
   737  		allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
   738  	}
   739  
   740  	return allErrs
   741  }
   742  
   743  func validateHostPathVolumeSource(hostPath *core.HostPathVolumeSource, fldPath *field.Path) field.ErrorList {
   744  	allErrs := field.ErrorList{}
   745  	if len(hostPath.Path) == 0 {
   746  		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
   747  		return allErrs
   748  	}
   749  
   750  	allErrs = append(allErrs, validatePathNoBacksteps(hostPath.Path, fldPath.Child("path"))...)
   751  	allErrs = append(allErrs, validateHostPathType(hostPath.Type, fldPath.Child("type"))...)
   752  	return allErrs
   753  }
   754  
   755  func validateGitRepoVolumeSource(gitRepo *core.GitRepoVolumeSource, fldPath *field.Path) field.ErrorList {
   756  	allErrs := field.ErrorList{}
   757  	if len(gitRepo.Repository) == 0 {
   758  		allErrs = append(allErrs, field.Required(fldPath.Child("repository"), ""))
   759  	}
   760  
   761  	pathErrs := validateLocalDescendingPath(gitRepo.Directory, fldPath.Child("directory"))
   762  	allErrs = append(allErrs, pathErrs...)
   763  	return allErrs
   764  }
   765  
   766  func validateISCSIVolumeSource(iscsi *core.ISCSIVolumeSource, fldPath *field.Path) field.ErrorList {
   767  	allErrs := field.ErrorList{}
   768  	if len(iscsi.TargetPortal) == 0 {
   769  		allErrs = append(allErrs, field.Required(fldPath.Child("targetPortal"), ""))
   770  	}
   771  	if len(iscsi.IQN) == 0 {
   772  		allErrs = append(allErrs, field.Required(fldPath.Child("iqn"), ""))
   773  	} else {
   774  		if !strings.HasPrefix(iscsi.IQN, "iqn") && !strings.HasPrefix(iscsi.IQN, "eui") && !strings.HasPrefix(iscsi.IQN, "naa") {
   775  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format starting with iqn, eui, or naa"))
   776  		} else if strings.HasPrefix(iscsi.IQN, "iqn") && !iscsiInitiatorIqnRegex.MatchString(iscsi.IQN) {
   777  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
   778  		} else if strings.HasPrefix(iscsi.IQN, "eui") && !iscsiInitiatorEuiRegex.MatchString(iscsi.IQN) {
   779  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
   780  		} else if strings.HasPrefix(iscsi.IQN, "naa") && !iscsiInitiatorNaaRegex.MatchString(iscsi.IQN) {
   781  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
   782  		}
   783  	}
   784  	if iscsi.Lun < 0 || iscsi.Lun > 255 {
   785  		allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), iscsi.Lun, validation.InclusiveRangeError(0, 255)))
   786  	}
   787  	if (iscsi.DiscoveryCHAPAuth || iscsi.SessionCHAPAuth) && iscsi.SecretRef == nil {
   788  		allErrs = append(allErrs, field.Required(fldPath.Child("secretRef"), ""))
   789  	}
   790  	if iscsi.InitiatorName != nil {
   791  		initiator := *iscsi.InitiatorName
   792  		if !strings.HasPrefix(initiator, "iqn") && !strings.HasPrefix(initiator, "eui") && !strings.HasPrefix(initiator, "naa") {
   793  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format starting with iqn, eui, or naa"))
   794  		}
   795  		if strings.HasPrefix(initiator, "iqn") && !iscsiInitiatorIqnRegex.MatchString(initiator) {
   796  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
   797  		} else if strings.HasPrefix(initiator, "eui") && !iscsiInitiatorEuiRegex.MatchString(initiator) {
   798  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
   799  		} else if strings.HasPrefix(initiator, "naa") && !iscsiInitiatorNaaRegex.MatchString(initiator) {
   800  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
   801  		}
   802  	}
   803  	return allErrs
   804  }
   805  
   806  func validateISCSIPersistentVolumeSource(iscsi *core.ISCSIPersistentVolumeSource, pvName string, fldPath *field.Path) field.ErrorList {
   807  	allErrs := field.ErrorList{}
   808  	if len(iscsi.TargetPortal) == 0 {
   809  		allErrs = append(allErrs, field.Required(fldPath.Child("targetPortal"), ""))
   810  	}
   811  	if iscsi.InitiatorName != nil && len(pvName+":"+iscsi.TargetPortal) > 64 {
   812  		tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
   813  		allErrs = append(allErrs, field.Invalid(fldPath.Child("targetportal"), iscsi.TargetPortal, tooLongErr))
   814  	}
   815  	if len(iscsi.IQN) == 0 {
   816  		allErrs = append(allErrs, field.Required(fldPath.Child("iqn"), ""))
   817  	} else {
   818  		if !strings.HasPrefix(iscsi.IQN, "iqn") && !strings.HasPrefix(iscsi.IQN, "eui") && !strings.HasPrefix(iscsi.IQN, "naa") {
   819  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
   820  		} else if strings.HasPrefix(iscsi.IQN, "iqn") && !iscsiInitiatorIqnRegex.MatchString(iscsi.IQN) {
   821  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
   822  		} else if strings.HasPrefix(iscsi.IQN, "eui") && !iscsiInitiatorEuiRegex.MatchString(iscsi.IQN) {
   823  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
   824  		} else if strings.HasPrefix(iscsi.IQN, "naa") && !iscsiInitiatorNaaRegex.MatchString(iscsi.IQN) {
   825  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
   826  		}
   827  	}
   828  	if iscsi.Lun < 0 || iscsi.Lun > 255 {
   829  		allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), iscsi.Lun, validation.InclusiveRangeError(0, 255)))
   830  	}
   831  	if (iscsi.DiscoveryCHAPAuth || iscsi.SessionCHAPAuth) && iscsi.SecretRef == nil {
   832  		allErrs = append(allErrs, field.Required(fldPath.Child("secretRef"), ""))
   833  	}
   834  	if iscsi.SecretRef != nil {
   835  		if len(iscsi.SecretRef.Name) == 0 {
   836  			allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
   837  		}
   838  	}
   839  	if iscsi.InitiatorName != nil {
   840  		initiator := *iscsi.InitiatorName
   841  		if !strings.HasPrefix(initiator, "iqn") && !strings.HasPrefix(initiator, "eui") && !strings.HasPrefix(initiator, "naa") {
   842  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
   843  		}
   844  		if strings.HasPrefix(initiator, "iqn") && !iscsiInitiatorIqnRegex.MatchString(initiator) {
   845  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
   846  		} else if strings.HasPrefix(initiator, "eui") && !iscsiInitiatorEuiRegex.MatchString(initiator) {
   847  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
   848  		} else if strings.HasPrefix(initiator, "naa") && !iscsiInitiatorNaaRegex.MatchString(initiator) {
   849  			allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
   850  		}
   851  	}
   852  	return allErrs
   853  }
   854  
   855  func validateFCVolumeSource(fc *core.FCVolumeSource, fldPath *field.Path) field.ErrorList {
   856  	allErrs := field.ErrorList{}
   857  	if len(fc.TargetWWNs) < 1 && len(fc.WWIDs) < 1 {
   858  		allErrs = append(allErrs, field.Required(fldPath.Child("targetWWNs"), "must specify either targetWWNs or wwids, but not both"))
   859  	}
   860  
   861  	if len(fc.TargetWWNs) != 0 && len(fc.WWIDs) != 0 {
   862  		allErrs = append(allErrs, field.Invalid(fldPath.Child("targetWWNs"), fc.TargetWWNs, "targetWWNs and wwids can not be specified simultaneously"))
   863  	}
   864  
   865  	if len(fc.TargetWWNs) != 0 {
   866  		if fc.Lun == nil {
   867  			allErrs = append(allErrs, field.Required(fldPath.Child("lun"), "lun is required if targetWWNs is specified"))
   868  		} else {
   869  			if *fc.Lun < 0 || *fc.Lun > 255 {
   870  				allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), fc.Lun, validation.InclusiveRangeError(0, 255)))
   871  			}
   872  		}
   873  	}
   874  	return allErrs
   875  }
   876  
   877  func validateGCEPersistentDiskVolumeSource(pd *core.GCEPersistentDiskVolumeSource, fldPath *field.Path) field.ErrorList {
   878  	allErrs := field.ErrorList{}
   879  	if len(pd.PDName) == 0 {
   880  		allErrs = append(allErrs, field.Required(fldPath.Child("pdName"), ""))
   881  	}
   882  	if pd.Partition < 0 || pd.Partition > 255 {
   883  		allErrs = append(allErrs, field.Invalid(fldPath.Child("partition"), pd.Partition, pdPartitionErrorMsg))
   884  	}
   885  	return allErrs
   886  }
   887  
   888  func validateAWSElasticBlockStoreVolumeSource(PD *core.AWSElasticBlockStoreVolumeSource, fldPath *field.Path) field.ErrorList {
   889  	allErrs := field.ErrorList{}
   890  	if len(PD.VolumeID) == 0 {
   891  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
   892  	}
   893  	if PD.Partition < 0 || PD.Partition > 255 {
   894  		allErrs = append(allErrs, field.Invalid(fldPath.Child("partition"), PD.Partition, pdPartitionErrorMsg))
   895  	}
   896  	return allErrs
   897  }
   898  
   899  func validateSecretVolumeSource(secretSource *core.SecretVolumeSource, fldPath *field.Path) field.ErrorList {
   900  	allErrs := field.ErrorList{}
   901  	if len(secretSource.SecretName) == 0 {
   902  		allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
   903  	}
   904  
   905  	secretMode := secretSource.DefaultMode
   906  	if secretMode != nil && (*secretMode > 0777 || *secretMode < 0) {
   907  		allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *secretMode, fileModeErrorMsg))
   908  	}
   909  
   910  	itemsPath := fldPath.Child("items")
   911  	for i, kp := range secretSource.Items {
   912  		itemPath := itemsPath.Index(i)
   913  		allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
   914  	}
   915  	return allErrs
   916  }
   917  
   918  func validateConfigMapVolumeSource(configMapSource *core.ConfigMapVolumeSource, fldPath *field.Path) field.ErrorList {
   919  	allErrs := field.ErrorList{}
   920  	if len(configMapSource.Name) == 0 {
   921  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
   922  	}
   923  
   924  	configMapMode := configMapSource.DefaultMode
   925  	if configMapMode != nil && (*configMapMode > 0777 || *configMapMode < 0) {
   926  		allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *configMapMode, fileModeErrorMsg))
   927  	}
   928  
   929  	itemsPath := fldPath.Child("items")
   930  	for i, kp := range configMapSource.Items {
   931  		itemPath := itemsPath.Index(i)
   932  		allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
   933  	}
   934  	return allErrs
   935  }
   936  
   937  func validateKeyToPath(kp *core.KeyToPath, fldPath *field.Path) field.ErrorList {
   938  	allErrs := field.ErrorList{}
   939  	if len(kp.Key) == 0 {
   940  		allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
   941  	}
   942  	if len(kp.Path) == 0 {
   943  		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
   944  	}
   945  	allErrs = append(allErrs, validateLocalNonReservedPath(kp.Path, fldPath.Child("path"))...)
   946  	if kp.Mode != nil && (*kp.Mode > 0777 || *kp.Mode < 0) {
   947  		allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *kp.Mode, fileModeErrorMsg))
   948  	}
   949  
   950  	return allErrs
   951  }
   952  
   953  func validatePersistentClaimVolumeSource(claim *core.PersistentVolumeClaimVolumeSource, fldPath *field.Path) field.ErrorList {
   954  	allErrs := field.ErrorList{}
   955  	if len(claim.ClaimName) == 0 {
   956  		allErrs = append(allErrs, field.Required(fldPath.Child("claimName"), ""))
   957  	}
   958  	return allErrs
   959  }
   960  
   961  func validateNFSVolumeSource(nfs *core.NFSVolumeSource, fldPath *field.Path) field.ErrorList {
   962  	allErrs := field.ErrorList{}
   963  	if len(nfs.Server) == 0 {
   964  		allErrs = append(allErrs, field.Required(fldPath.Child("server"), ""))
   965  	}
   966  	if len(nfs.Path) == 0 {
   967  		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
   968  	}
   969  	if !path.IsAbs(nfs.Path) {
   970  		allErrs = append(allErrs, field.Invalid(fldPath.Child("path"), nfs.Path, "must be an absolute path"))
   971  	}
   972  	return allErrs
   973  }
   974  
   975  func validateQuobyteVolumeSource(quobyte *core.QuobyteVolumeSource, fldPath *field.Path) field.ErrorList {
   976  	allErrs := field.ErrorList{}
   977  	if len(quobyte.Registry) == 0 {
   978  		allErrs = append(allErrs, field.Required(fldPath.Child("registry"), "must be a host:port pair or multiple pairs separated by commas"))
   979  	} else if len(quobyte.Tenant) >= 65 {
   980  		allErrs = append(allErrs, field.Required(fldPath.Child("tenant"), "must be a UUID and may not exceed a length of 64 characters"))
   981  	} else {
   982  		for _, hostPortPair := range strings.Split(quobyte.Registry, ",") {
   983  			if _, _, err := net.SplitHostPort(hostPortPair); err != nil {
   984  				allErrs = append(allErrs, field.Invalid(fldPath.Child("registry"), quobyte.Registry, "must be a host:port pair or multiple pairs separated by commas"))
   985  			}
   986  		}
   987  	}
   988  
   989  	if len(quobyte.Volume) == 0 {
   990  		allErrs = append(allErrs, field.Required(fldPath.Child("volume"), ""))
   991  	}
   992  	return allErrs
   993  }
   994  
   995  func validateGlusterfsVolumeSource(glusterfs *core.GlusterfsVolumeSource, fldPath *field.Path) field.ErrorList {
   996  	allErrs := field.ErrorList{}
   997  	if len(glusterfs.EndpointsName) == 0 {
   998  		allErrs = append(allErrs, field.Required(fldPath.Child("endpoints"), ""))
   999  	}
  1000  	if len(glusterfs.Path) == 0 {
  1001  		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  1002  	}
  1003  	return allErrs
  1004  }
  1005  func validateGlusterfsPersistentVolumeSource(glusterfs *core.GlusterfsPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1006  	allErrs := field.ErrorList{}
  1007  	if len(glusterfs.EndpointsName) == 0 {
  1008  		allErrs = append(allErrs, field.Required(fldPath.Child("endpoints"), ""))
  1009  	}
  1010  	if len(glusterfs.Path) == 0 {
  1011  		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  1012  	}
  1013  	if glusterfs.EndpointsNamespace != nil {
  1014  		endpointNs := glusterfs.EndpointsNamespace
  1015  		if *endpointNs == "" {
  1016  			allErrs = append(allErrs, field.Invalid(fldPath.Child("endpointsNamespace"), *endpointNs, "if the endpointnamespace is set, it must be a valid namespace name"))
  1017  		} else {
  1018  			for _, msg := range ValidateNamespaceName(*endpointNs, false) {
  1019  				allErrs = append(allErrs, field.Invalid(fldPath.Child("endpointsNamespace"), *endpointNs, msg))
  1020  			}
  1021  		}
  1022  	}
  1023  	return allErrs
  1024  }
  1025  
  1026  func validateFlockerVolumeSource(flocker *core.FlockerVolumeSource, fldPath *field.Path) field.ErrorList {
  1027  	allErrs := field.ErrorList{}
  1028  	if len(flocker.DatasetName) == 0 && len(flocker.DatasetUUID) == 0 {
  1029  		// TODO: consider adding a RequiredOneOf() error for this and similar cases
  1030  		allErrs = append(allErrs, field.Required(fldPath, "one of datasetName and datasetUUID is required"))
  1031  	}
  1032  	if len(flocker.DatasetName) != 0 && len(flocker.DatasetUUID) != 0 {
  1033  		allErrs = append(allErrs, field.Invalid(fldPath, "resource", "datasetName and datasetUUID can not be specified simultaneously"))
  1034  	}
  1035  	if strings.Contains(flocker.DatasetName, "/") {
  1036  		allErrs = append(allErrs, field.Invalid(fldPath.Child("datasetName"), flocker.DatasetName, "must not contain '/'"))
  1037  	}
  1038  	return allErrs
  1039  }
  1040  
  1041  var validVolumeDownwardAPIFieldPathExpressions = sets.New(
  1042  	"metadata.name",
  1043  	"metadata.namespace",
  1044  	"metadata.labels",
  1045  	"metadata.annotations",
  1046  	"metadata.uid")
  1047  
  1048  func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  1049  	allErrs := field.ErrorList{}
  1050  	if len(file.Path) == 0 {
  1051  		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  1052  	}
  1053  	allErrs = append(allErrs, validateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
  1054  	if file.FieldRef != nil {
  1055  		allErrs = append(allErrs, validateObjectFieldSelector(file.FieldRef, &validVolumeDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
  1056  		if file.ResourceFieldRef != nil {
  1057  			allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
  1058  		}
  1059  		allErrs = append(allErrs, validateDownwardAPIHostIPs(file.FieldRef, fldPath.Child("fieldRef"), opts)...)
  1060  	} else if file.ResourceFieldRef != nil {
  1061  		localValidContainerResourceFieldPathPrefixes := validContainerResourceFieldPathPrefixesWithDownwardAPIHugePages
  1062  		allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, &localValidContainerResourceFieldPathPrefixes, fldPath.Child("resourceFieldRef"), true)...)
  1063  	} else {
  1064  		allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
  1065  	}
  1066  	if file.Mode != nil && (*file.Mode > 0777 || *file.Mode < 0) {
  1067  		allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, fileModeErrorMsg))
  1068  	}
  1069  
  1070  	return allErrs
  1071  }
  1072  
  1073  func validateDownwardAPIVolumeSource(downwardAPIVolume *core.DownwardAPIVolumeSource, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  1074  	allErrs := field.ErrorList{}
  1075  
  1076  	downwardAPIMode := downwardAPIVolume.DefaultMode
  1077  	if downwardAPIMode != nil && (*downwardAPIMode > 0777 || *downwardAPIMode < 0) {
  1078  		allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *downwardAPIMode, fileModeErrorMsg))
  1079  	}
  1080  
  1081  	for _, file := range downwardAPIVolume.Items {
  1082  		allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, fldPath, opts)...)
  1083  	}
  1084  	return allErrs
  1085  }
  1086  
  1087  func validateProjectionSources(projection *core.ProjectedVolumeSource, projectionMode *int32, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  1088  	allErrs := field.ErrorList{}
  1089  	allPaths := sets.Set[string]{}
  1090  
  1091  	for i, source := range projection.Sources {
  1092  		numSources := 0
  1093  		srcPath := fldPath.Child("sources").Index(i)
  1094  		if projPath := srcPath.Child("secret"); source.Secret != nil {
  1095  			numSources++
  1096  			if len(source.Secret.Name) == 0 {
  1097  				allErrs = append(allErrs, field.Required(projPath.Child("name"), ""))
  1098  			}
  1099  			itemsPath := projPath.Child("items")
  1100  			for i, kp := range source.Secret.Items {
  1101  				itemPath := itemsPath.Index(i)
  1102  				allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
  1103  				if len(kp.Path) > 0 {
  1104  					curPath := kp.Path
  1105  					if !allPaths.Has(curPath) {
  1106  						allPaths.Insert(curPath)
  1107  					} else {
  1108  						allErrs = append(allErrs, field.Invalid(fldPath, source.Secret.Name, "conflicting duplicate paths"))
  1109  					}
  1110  				}
  1111  			}
  1112  		}
  1113  		if projPath := srcPath.Child("configMap"); source.ConfigMap != nil {
  1114  			numSources++
  1115  			if len(source.ConfigMap.Name) == 0 {
  1116  				allErrs = append(allErrs, field.Required(projPath.Child("name"), ""))
  1117  			}
  1118  			itemsPath := projPath.Child("items")
  1119  			for i, kp := range source.ConfigMap.Items {
  1120  				itemPath := itemsPath.Index(i)
  1121  				allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
  1122  				if len(kp.Path) > 0 {
  1123  					curPath := kp.Path
  1124  					if !allPaths.Has(curPath) {
  1125  						allPaths.Insert(curPath)
  1126  					} else {
  1127  						allErrs = append(allErrs, field.Invalid(fldPath, source.ConfigMap.Name, "conflicting duplicate paths"))
  1128  					}
  1129  				}
  1130  			}
  1131  		}
  1132  		if projPath := srcPath.Child("downwardAPI"); source.DownwardAPI != nil {
  1133  			numSources++
  1134  			for _, file := range source.DownwardAPI.Items {
  1135  				allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, projPath, opts)...)
  1136  				if len(file.Path) > 0 {
  1137  					curPath := file.Path
  1138  					if !allPaths.Has(curPath) {
  1139  						allPaths.Insert(curPath)
  1140  					} else {
  1141  						allErrs = append(allErrs, field.Invalid(fldPath, curPath, "conflicting duplicate paths"))
  1142  					}
  1143  				}
  1144  			}
  1145  		}
  1146  		if projPath := srcPath.Child("serviceAccountToken"); source.ServiceAccountToken != nil {
  1147  			numSources++
  1148  			if source.ServiceAccountToken.ExpirationSeconds < 10*60 {
  1149  				allErrs = append(allErrs, field.Invalid(projPath.Child("expirationSeconds"), source.ServiceAccountToken.ExpirationSeconds, "may not specify a duration less than 10 minutes"))
  1150  			}
  1151  			if source.ServiceAccountToken.ExpirationSeconds > 1<<32 {
  1152  				allErrs = append(allErrs, field.Invalid(projPath.Child("expirationSeconds"), source.ServiceAccountToken.ExpirationSeconds, "may not specify a duration larger than 2^32 seconds"))
  1153  			}
  1154  			if source.ServiceAccountToken.Path == "" {
  1155  				allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  1156  			}
  1157  		}
  1158  		if projPath := srcPath.Child("clusterTrustBundlePEM"); source.ClusterTrustBundle != nil {
  1159  			numSources++
  1160  
  1161  			usingName := source.ClusterTrustBundle.Name != nil
  1162  			usingSignerName := source.ClusterTrustBundle.SignerName != nil
  1163  
  1164  			switch {
  1165  			case usingName && usingSignerName:
  1166  				allErrs = append(allErrs, field.Invalid(projPath, source.ClusterTrustBundle, "only one of name and signerName may be used"))
  1167  			case usingName:
  1168  				if *source.ClusterTrustBundle.Name == "" {
  1169  					allErrs = append(allErrs, field.Required(projPath.Child("name"), "must be a valid object name"))
  1170  				}
  1171  
  1172  				name := *source.ClusterTrustBundle.Name
  1173  				if signerName, ok := extractSignerNameFromClusterTrustBundleName(name); ok {
  1174  					validationFunc := ValidateClusterTrustBundleName(signerName)
  1175  					errMsgs := validationFunc(name, false)
  1176  					for _, msg := range errMsgs {
  1177  						allErrs = append(allErrs, field.Invalid(projPath.Child("name"), name, fmt.Sprintf("not a valid clustertrustbundlename: %v", msg)))
  1178  					}
  1179  				} else {
  1180  					validationFunc := ValidateClusterTrustBundleName("")
  1181  					errMsgs := validationFunc(name, false)
  1182  					for _, msg := range errMsgs {
  1183  						allErrs = append(allErrs, field.Invalid(projPath.Child("name"), name, fmt.Sprintf("not a valid clustertrustbundlename: %v", msg)))
  1184  					}
  1185  				}
  1186  
  1187  				if source.ClusterTrustBundle.LabelSelector != nil {
  1188  					allErrs = append(allErrs, field.Invalid(projPath.Child("labelSelector"), source.ClusterTrustBundle.LabelSelector, "labelSelector must be unset if name is specified"))
  1189  				}
  1190  			case usingSignerName:
  1191  				if *source.ClusterTrustBundle.SignerName == "" {
  1192  					allErrs = append(allErrs, field.Required(projPath.Child("signerName"), "must be a valid signer name"))
  1193  				}
  1194  
  1195  				allErrs = append(allErrs, ValidateSignerName(projPath.Child("signerName"), *source.ClusterTrustBundle.SignerName)...)
  1196  
  1197  				labelSelectorErrs := unversionedvalidation.ValidateLabelSelector(
  1198  					source.ClusterTrustBundle.LabelSelector,
  1199  					unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false},
  1200  					projPath.Child("labelSelector"),
  1201  				)
  1202  				allErrs = append(allErrs, labelSelectorErrs...)
  1203  
  1204  			default:
  1205  				allErrs = append(allErrs, field.Required(projPath, "either name or signerName must be specified"))
  1206  			}
  1207  
  1208  			if source.ClusterTrustBundle.Path == "" {
  1209  				allErrs = append(allErrs, field.Required(projPath.Child("path"), ""))
  1210  			}
  1211  
  1212  			allErrs = append(allErrs, validateLocalNonReservedPath(source.ClusterTrustBundle.Path, projPath.Child("path"))...)
  1213  
  1214  			curPath := source.ClusterTrustBundle.Path
  1215  			if !allPaths.Has(curPath) {
  1216  				allPaths.Insert(curPath)
  1217  			} else {
  1218  				allErrs = append(allErrs, field.Invalid(fldPath, curPath, "conflicting duplicate paths"))
  1219  			}
  1220  		}
  1221  		if numSources > 1 {
  1222  			allErrs = append(allErrs, field.Forbidden(srcPath, "may not specify more than 1 volume type"))
  1223  		}
  1224  	}
  1225  	return allErrs
  1226  }
  1227  
  1228  func validateProjectedVolumeSource(projection *core.ProjectedVolumeSource, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  1229  	allErrs := field.ErrorList{}
  1230  
  1231  	projectionMode := projection.DefaultMode
  1232  	if projectionMode != nil && (*projectionMode > 0777 || *projectionMode < 0) {
  1233  		allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *projectionMode, fileModeErrorMsg))
  1234  	}
  1235  
  1236  	allErrs = append(allErrs, validateProjectionSources(projection, projectionMode, fldPath, opts)...)
  1237  	return allErrs
  1238  }
  1239  
  1240  var supportedHostPathTypes = sets.New(
  1241  	core.HostPathUnset,
  1242  	core.HostPathDirectoryOrCreate,
  1243  	core.HostPathDirectory,
  1244  	core.HostPathFileOrCreate,
  1245  	core.HostPathFile,
  1246  	core.HostPathSocket,
  1247  	core.HostPathCharDev,
  1248  	core.HostPathBlockDev)
  1249  
  1250  func validateHostPathType(hostPathType *core.HostPathType, fldPath *field.Path) field.ErrorList {
  1251  	allErrs := field.ErrorList{}
  1252  
  1253  	if hostPathType != nil && !supportedHostPathTypes.Has(*hostPathType) {
  1254  		allErrs = append(allErrs, field.NotSupported(fldPath, hostPathType, sets.List(supportedHostPathTypes)))
  1255  	}
  1256  
  1257  	return allErrs
  1258  }
  1259  
  1260  // This validate will make sure targetPath:
  1261  // 1. is not abs path
  1262  // 2. does not have any element which is ".."
  1263  func validateLocalDescendingPath(targetPath string, fldPath *field.Path) field.ErrorList {
  1264  	allErrs := field.ErrorList{}
  1265  	if path.IsAbs(targetPath) {
  1266  		allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
  1267  	}
  1268  
  1269  	allErrs = append(allErrs, validatePathNoBacksteps(targetPath, fldPath)...)
  1270  
  1271  	return allErrs
  1272  }
  1273  
  1274  // validatePathNoBacksteps makes sure the targetPath does not have any `..` path elements when split
  1275  //
  1276  // This assumes the OS of the apiserver and the nodes are the same. The same check should be done
  1277  // on the node to ensure there are no backsteps.
  1278  func validatePathNoBacksteps(targetPath string, fldPath *field.Path) field.ErrorList {
  1279  	allErrs := field.ErrorList{}
  1280  	parts := strings.Split(filepath.ToSlash(targetPath), "/")
  1281  	for _, item := range parts {
  1282  		if item == ".." {
  1283  			allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '..'"))
  1284  			break // even for `../../..`, one error is sufficient to make the point
  1285  		}
  1286  	}
  1287  	return allErrs
  1288  }
  1289  
  1290  // validateMountPropagation verifies that MountPropagation field is valid and
  1291  // allowed for given container.
  1292  func validateMountPropagation(mountPropagation *core.MountPropagationMode, container *core.Container, fldPath *field.Path) field.ErrorList {
  1293  	allErrs := field.ErrorList{}
  1294  
  1295  	if mountPropagation == nil {
  1296  		return allErrs
  1297  	}
  1298  
  1299  	supportedMountPropagations := sets.New(
  1300  		core.MountPropagationBidirectional,
  1301  		core.MountPropagationHostToContainer,
  1302  		core.MountPropagationNone)
  1303  
  1304  	if !supportedMountPropagations.Has(*mountPropagation) {
  1305  		allErrs = append(allErrs, field.NotSupported(fldPath, *mountPropagation, sets.List(supportedMountPropagations)))
  1306  	}
  1307  
  1308  	if container == nil {
  1309  		// The container is not available yet.
  1310  		// Stop validation now, Pod validation will refuse final
  1311  		// Pods with Bidirectional propagation in non-privileged containers.
  1312  		return allErrs
  1313  	}
  1314  
  1315  	privileged := container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged
  1316  	if *mountPropagation == core.MountPropagationBidirectional && !privileged {
  1317  		allErrs = append(allErrs, field.Forbidden(fldPath, "Bidirectional mount propagation is available only to privileged containers"))
  1318  	}
  1319  	return allErrs
  1320  }
  1321  
  1322  // This validate will make sure targetPath:
  1323  // 1. is not abs path
  1324  // 2. does not contain any '..' elements
  1325  // 3. does not start with '..'
  1326  func validateLocalNonReservedPath(targetPath string, fldPath *field.Path) field.ErrorList {
  1327  	allErrs := field.ErrorList{}
  1328  	allErrs = append(allErrs, validateLocalDescendingPath(targetPath, fldPath)...)
  1329  	// Don't report this error if the check for .. elements already caught it.
  1330  	if strings.HasPrefix(targetPath, "..") && !strings.HasPrefix(targetPath, "../") {
  1331  		allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '..'"))
  1332  	}
  1333  	return allErrs
  1334  }
  1335  
  1336  func validateRBDVolumeSource(rbd *core.RBDVolumeSource, fldPath *field.Path) field.ErrorList {
  1337  	allErrs := field.ErrorList{}
  1338  	if len(rbd.CephMonitors) == 0 {
  1339  		allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1340  	}
  1341  	if len(rbd.RBDImage) == 0 {
  1342  		allErrs = append(allErrs, field.Required(fldPath.Child("image"), ""))
  1343  	}
  1344  	return allErrs
  1345  }
  1346  
  1347  func validateRBDPersistentVolumeSource(rbd *core.RBDPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1348  	allErrs := field.ErrorList{}
  1349  	if len(rbd.CephMonitors) == 0 {
  1350  		allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1351  	}
  1352  	if len(rbd.RBDImage) == 0 {
  1353  		allErrs = append(allErrs, field.Required(fldPath.Child("image"), ""))
  1354  	}
  1355  	return allErrs
  1356  }
  1357  
  1358  func validateCinderVolumeSource(cd *core.CinderVolumeSource, fldPath *field.Path) field.ErrorList {
  1359  	allErrs := field.ErrorList{}
  1360  	if len(cd.VolumeID) == 0 {
  1361  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
  1362  	}
  1363  	if cd.SecretRef != nil {
  1364  		if len(cd.SecretRef.Name) == 0 {
  1365  			allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1366  		}
  1367  	}
  1368  	return allErrs
  1369  }
  1370  
  1371  func validateCinderPersistentVolumeSource(cd *core.CinderPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1372  	allErrs := field.ErrorList{}
  1373  	if len(cd.VolumeID) == 0 {
  1374  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
  1375  	}
  1376  	if cd.SecretRef != nil {
  1377  		if len(cd.SecretRef.Name) == 0 {
  1378  			allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1379  		}
  1380  		if len(cd.SecretRef.Namespace) == 0 {
  1381  			allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "namespace"), ""))
  1382  		}
  1383  	}
  1384  	return allErrs
  1385  }
  1386  
  1387  func validateCephFSVolumeSource(cephfs *core.CephFSVolumeSource, fldPath *field.Path) field.ErrorList {
  1388  	allErrs := field.ErrorList{}
  1389  	if len(cephfs.Monitors) == 0 {
  1390  		allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1391  	}
  1392  	return allErrs
  1393  }
  1394  
  1395  func validateCephFSPersistentVolumeSource(cephfs *core.CephFSPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1396  	allErrs := field.ErrorList{}
  1397  	if len(cephfs.Monitors) == 0 {
  1398  		allErrs = append(allErrs, field.Required(fldPath.Child("monitors"), ""))
  1399  	}
  1400  	return allErrs
  1401  }
  1402  
  1403  func validateFlexVolumeSource(fv *core.FlexVolumeSource, fldPath *field.Path) field.ErrorList {
  1404  	allErrs := field.ErrorList{}
  1405  	if len(fv.Driver) == 0 {
  1406  		allErrs = append(allErrs, field.Required(fldPath.Child("driver"), ""))
  1407  	}
  1408  
  1409  	// Make sure user-specified options don't use kubernetes namespaces
  1410  	for k := range fv.Options {
  1411  		namespace := k
  1412  		if parts := strings.SplitN(k, "/", 2); len(parts) == 2 {
  1413  			namespace = parts[0]
  1414  		}
  1415  		normalized := "." + strings.ToLower(namespace)
  1416  		if strings.HasSuffix(normalized, ".kubernetes.io") || strings.HasSuffix(normalized, ".k8s.io") {
  1417  			allErrs = append(allErrs, field.Invalid(fldPath.Child("options").Key(k), k, "kubernetes.io and k8s.io namespaces are reserved"))
  1418  		}
  1419  	}
  1420  
  1421  	return allErrs
  1422  }
  1423  
  1424  func validateFlexPersistentVolumeSource(fv *core.FlexPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1425  	allErrs := field.ErrorList{}
  1426  	if len(fv.Driver) == 0 {
  1427  		allErrs = append(allErrs, field.Required(fldPath.Child("driver"), ""))
  1428  	}
  1429  
  1430  	// Make sure user-specified options don't use kubernetes namespaces
  1431  	for k := range fv.Options {
  1432  		namespace := k
  1433  		if parts := strings.SplitN(k, "/", 2); len(parts) == 2 {
  1434  			namespace = parts[0]
  1435  		}
  1436  		normalized := "." + strings.ToLower(namespace)
  1437  		if strings.HasSuffix(normalized, ".kubernetes.io") || strings.HasSuffix(normalized, ".k8s.io") {
  1438  			allErrs = append(allErrs, field.Invalid(fldPath.Child("options").Key(k), k, "kubernetes.io and k8s.io namespaces are reserved"))
  1439  		}
  1440  	}
  1441  
  1442  	return allErrs
  1443  }
  1444  
  1445  func validateAzureFile(azure *core.AzureFileVolumeSource, fldPath *field.Path) field.ErrorList {
  1446  	allErrs := field.ErrorList{}
  1447  	if azure.SecretName == "" {
  1448  		allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
  1449  	}
  1450  	if azure.ShareName == "" {
  1451  		allErrs = append(allErrs, field.Required(fldPath.Child("shareName"), ""))
  1452  	}
  1453  	return allErrs
  1454  }
  1455  
  1456  func validateAzureFilePV(azure *core.AzureFilePersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1457  	allErrs := field.ErrorList{}
  1458  	if azure.SecretName == "" {
  1459  		allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
  1460  	}
  1461  	if azure.ShareName == "" {
  1462  		allErrs = append(allErrs, field.Required(fldPath.Child("shareName"), ""))
  1463  	}
  1464  	if azure.SecretNamespace != nil {
  1465  		if len(*azure.SecretNamespace) == 0 {
  1466  			allErrs = append(allErrs, field.Required(fldPath.Child("secretNamespace"), ""))
  1467  		}
  1468  	}
  1469  	return allErrs
  1470  }
  1471  
  1472  func validateAzureDisk(azure *core.AzureDiskVolumeSource, fldPath *field.Path) field.ErrorList {
  1473  	var supportedCachingModes = sets.New(
  1474  		core.AzureDataDiskCachingNone,
  1475  		core.AzureDataDiskCachingReadOnly,
  1476  		core.AzureDataDiskCachingReadWrite)
  1477  
  1478  	var supportedDiskKinds = sets.New(
  1479  		core.AzureSharedBlobDisk,
  1480  		core.AzureDedicatedBlobDisk,
  1481  		core.AzureManagedDisk)
  1482  
  1483  	diskURISupportedManaged := []string{"/subscriptions/{sub-id}/resourcegroups/{group-name}/providers/microsoft.compute/disks/{disk-id}"}
  1484  	diskURISupportedblob := []string{"https://{account-name}.blob.core.windows.net/{container-name}/{disk-name}.vhd"}
  1485  
  1486  	allErrs := field.ErrorList{}
  1487  	if azure.DiskName == "" {
  1488  		allErrs = append(allErrs, field.Required(fldPath.Child("diskName"), ""))
  1489  	}
  1490  
  1491  	if azure.DataDiskURI == "" {
  1492  		allErrs = append(allErrs, field.Required(fldPath.Child("diskURI"), ""))
  1493  	}
  1494  
  1495  	if azure.CachingMode != nil && !supportedCachingModes.Has(*azure.CachingMode) {
  1496  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("cachingMode"), *azure.CachingMode, sets.List(supportedCachingModes)))
  1497  	}
  1498  
  1499  	if azure.Kind != nil && !supportedDiskKinds.Has(*azure.Kind) {
  1500  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("kind"), *azure.Kind, sets.List(supportedDiskKinds)))
  1501  	}
  1502  
  1503  	// validate that DiskUri is the correct format
  1504  	if azure.Kind != nil && *azure.Kind == core.AzureManagedDisk && strings.Index(azure.DataDiskURI, "/subscriptions/") != 0 {
  1505  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("diskURI"), azure.DataDiskURI, diskURISupportedManaged))
  1506  	}
  1507  
  1508  	if azure.Kind != nil && *azure.Kind != core.AzureManagedDisk && strings.Index(azure.DataDiskURI, "https://") != 0 {
  1509  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("diskURI"), azure.DataDiskURI, diskURISupportedblob))
  1510  	}
  1511  
  1512  	return allErrs
  1513  }
  1514  
  1515  func validateVsphereVolumeSource(cd *core.VsphereVirtualDiskVolumeSource, fldPath *field.Path) field.ErrorList {
  1516  	allErrs := field.ErrorList{}
  1517  	if len(cd.VolumePath) == 0 {
  1518  		allErrs = append(allErrs, field.Required(fldPath.Child("volumePath"), ""))
  1519  	}
  1520  	return allErrs
  1521  }
  1522  
  1523  func validatePhotonPersistentDiskVolumeSource(cd *core.PhotonPersistentDiskVolumeSource, fldPath *field.Path) field.ErrorList {
  1524  	allErrs := field.ErrorList{}
  1525  	if len(cd.PdID) == 0 {
  1526  		allErrs = append(allErrs, field.Required(fldPath.Child("pdID"), ""))
  1527  	}
  1528  	return allErrs
  1529  }
  1530  
  1531  func validatePortworxVolumeSource(pwx *core.PortworxVolumeSource, fldPath *field.Path) field.ErrorList {
  1532  	allErrs := field.ErrorList{}
  1533  	if len(pwx.VolumeID) == 0 {
  1534  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeID"), ""))
  1535  	}
  1536  	return allErrs
  1537  }
  1538  
  1539  func validateScaleIOVolumeSource(sio *core.ScaleIOVolumeSource, fldPath *field.Path) field.ErrorList {
  1540  	allErrs := field.ErrorList{}
  1541  	if sio.Gateway == "" {
  1542  		allErrs = append(allErrs, field.Required(fldPath.Child("gateway"), ""))
  1543  	}
  1544  	if sio.System == "" {
  1545  		allErrs = append(allErrs, field.Required(fldPath.Child("system"), ""))
  1546  	}
  1547  	if sio.VolumeName == "" {
  1548  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1549  	}
  1550  	return allErrs
  1551  }
  1552  
  1553  func validateScaleIOPersistentVolumeSource(sio *core.ScaleIOPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1554  	allErrs := field.ErrorList{}
  1555  	if sio.Gateway == "" {
  1556  		allErrs = append(allErrs, field.Required(fldPath.Child("gateway"), ""))
  1557  	}
  1558  	if sio.System == "" {
  1559  		allErrs = append(allErrs, field.Required(fldPath.Child("system"), ""))
  1560  	}
  1561  	if sio.VolumeName == "" {
  1562  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1563  	}
  1564  	return allErrs
  1565  }
  1566  
  1567  func validateLocalVolumeSource(ls *core.LocalVolumeSource, fldPath *field.Path) field.ErrorList {
  1568  	allErrs := field.ErrorList{}
  1569  	if ls.Path == "" {
  1570  		allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
  1571  		return allErrs
  1572  	}
  1573  
  1574  	allErrs = append(allErrs, validatePathNoBacksteps(ls.Path, fldPath.Child("path"))...)
  1575  	return allErrs
  1576  }
  1577  
  1578  func validateStorageOSVolumeSource(storageos *core.StorageOSVolumeSource, fldPath *field.Path) field.ErrorList {
  1579  	allErrs := field.ErrorList{}
  1580  	if len(storageos.VolumeName) == 0 {
  1581  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1582  	} else {
  1583  		allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeName, fldPath.Child("volumeName"))...)
  1584  	}
  1585  	if len(storageos.VolumeNamespace) > 0 {
  1586  		allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeNamespace, fldPath.Child("volumeNamespace"))...)
  1587  	}
  1588  	if storageos.SecretRef != nil {
  1589  		if len(storageos.SecretRef.Name) == 0 {
  1590  			allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1591  		}
  1592  	}
  1593  	return allErrs
  1594  }
  1595  
  1596  func validateStorageOSPersistentVolumeSource(storageos *core.StorageOSPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1597  	allErrs := field.ErrorList{}
  1598  	if len(storageos.VolumeName) == 0 {
  1599  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
  1600  	} else {
  1601  		allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeName, fldPath.Child("volumeName"))...)
  1602  	}
  1603  	if len(storageos.VolumeNamespace) > 0 {
  1604  		allErrs = append(allErrs, ValidateDNS1123Label(storageos.VolumeNamespace, fldPath.Child("volumeNamespace"))...)
  1605  	}
  1606  	if storageos.SecretRef != nil {
  1607  		if len(storageos.SecretRef.Name) == 0 {
  1608  			allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
  1609  		}
  1610  		if len(storageos.SecretRef.Namespace) == 0 {
  1611  			allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "namespace"), ""))
  1612  		}
  1613  	}
  1614  	return allErrs
  1615  }
  1616  
  1617  // validatePVSecretReference check whether provided SecretReference object is valid in terms of secret name and namespace.
  1618  
  1619  func validatePVSecretReference(secretRef *core.SecretReference, fldPath *field.Path) field.ErrorList {
  1620  	var allErrs field.ErrorList
  1621  	if len(secretRef.Name) == 0 {
  1622  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  1623  	} else {
  1624  		allErrs = append(allErrs, ValidateDNS1123Subdomain(secretRef.Name, fldPath.Child("name"))...)
  1625  	}
  1626  
  1627  	if len(secretRef.Namespace) == 0 {
  1628  		allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
  1629  	} else {
  1630  		allErrs = append(allErrs, ValidateDNS1123Label(secretRef.Namespace, fldPath.Child("namespace"))...)
  1631  	}
  1632  	return allErrs
  1633  }
  1634  
  1635  func ValidateCSIDriverName(driverName string, fldPath *field.Path) field.ErrorList {
  1636  	allErrs := field.ErrorList{}
  1637  
  1638  	if len(driverName) == 0 {
  1639  		allErrs = append(allErrs, field.Required(fldPath, ""))
  1640  	}
  1641  
  1642  	if len(driverName) > 63 {
  1643  		allErrs = append(allErrs, field.TooLong(fldPath, driverName, 63))
  1644  	}
  1645  
  1646  	for _, msg := range validation.IsDNS1123Subdomain(strings.ToLower(driverName)) {
  1647  		allErrs = append(allErrs, field.Invalid(fldPath, driverName, msg))
  1648  	}
  1649  
  1650  	return allErrs
  1651  }
  1652  
  1653  func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
  1654  	allErrs := field.ErrorList{}
  1655  
  1656  	allErrs = append(allErrs, ValidateCSIDriverName(csi.Driver, fldPath.Child("driver"))...)
  1657  
  1658  	if len(csi.VolumeHandle) == 0 {
  1659  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeHandle"), ""))
  1660  	}
  1661  	if csi.ControllerPublishSecretRef != nil {
  1662  		allErrs = append(allErrs, validatePVSecretReference(csi.ControllerPublishSecretRef, fldPath.Child("controllerPublishSecretRef"))...)
  1663  	}
  1664  	if csi.ControllerExpandSecretRef != nil {
  1665  		allErrs = append(allErrs, validatePVSecretReference(csi.ControllerExpandSecretRef, fldPath.Child("controllerExpandSecretRef"))...)
  1666  	}
  1667  	if csi.NodePublishSecretRef != nil {
  1668  		allErrs = append(allErrs, validatePVSecretReference(csi.NodePublishSecretRef, fldPath.Child("nodePublishSecretRef"))...)
  1669  	}
  1670  	if csi.NodeExpandSecretRef != nil {
  1671  		allErrs = append(allErrs, validatePVSecretReference(csi.NodeExpandSecretRef, fldPath.Child("nodeExpandSecretRef"))...)
  1672  	}
  1673  	return allErrs
  1674  }
  1675  
  1676  func validateCSIVolumeSource(csi *core.CSIVolumeSource, fldPath *field.Path) field.ErrorList {
  1677  	allErrs := field.ErrorList{}
  1678  	allErrs = append(allErrs, ValidateCSIDriverName(csi.Driver, fldPath.Child("driver"))...)
  1679  
  1680  	if csi.NodePublishSecretRef != nil {
  1681  		if len(csi.NodePublishSecretRef.Name) == 0 {
  1682  			allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef", "name"), ""))
  1683  		} else {
  1684  			for _, msg := range ValidateSecretName(csi.NodePublishSecretRef.Name, false) {
  1685  				allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), csi.NodePublishSecretRef.Name, msg))
  1686  			}
  1687  		}
  1688  	}
  1689  
  1690  	return allErrs
  1691  }
  1692  
  1693  func validateEphemeralVolumeSource(ephemeral *core.EphemeralVolumeSource, fldPath *field.Path) field.ErrorList {
  1694  	allErrs := field.ErrorList{}
  1695  	if ephemeral.VolumeClaimTemplate == nil {
  1696  		allErrs = append(allErrs, field.Required(fldPath.Child("volumeClaimTemplate"), ""))
  1697  	} else {
  1698  		opts := ValidationOptionsForPersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, nil)
  1699  		allErrs = append(allErrs, ValidatePersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, fldPath.Child("volumeClaimTemplate"), opts)...)
  1700  	}
  1701  	return allErrs
  1702  }
  1703  
  1704  // ValidatePersistentVolumeClaimTemplate verifies that the embedded object meta and spec are valid.
  1705  // Checking of the object data is very minimal because only labels and annotations are used.
  1706  func ValidatePersistentVolumeClaimTemplate(claimTemplate *core.PersistentVolumeClaimTemplate, fldPath *field.Path, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
  1707  	allErrs := ValidateTemplateObjectMeta(&claimTemplate.ObjectMeta, fldPath.Child("metadata"))
  1708  	allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&claimTemplate.Spec, fldPath.Child("spec"), opts)...)
  1709  	return allErrs
  1710  }
  1711  
  1712  func ValidateTemplateObjectMeta(objMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
  1713  	allErrs := apimachineryvalidation.ValidateAnnotations(objMeta.Annotations, fldPath.Child("annotations"))
  1714  	allErrs = append(allErrs, unversionedvalidation.ValidateLabels(objMeta.Labels, fldPath.Child("labels"))...)
  1715  	// All other fields are not supported and thus must not be set
  1716  	// to avoid confusion.  We could reject individual fields,
  1717  	// but then adding a new one to ObjectMeta wouldn't be checked
  1718  	// unless this code gets updated. Instead, we ensure that
  1719  	// only allowed fields are set via reflection.
  1720  	allErrs = append(allErrs, validateFieldAllowList(*objMeta, allowedTemplateObjectMetaFields, "cannot be set", fldPath)...)
  1721  	return allErrs
  1722  }
  1723  
  1724  var allowedTemplateObjectMetaFields = map[string]bool{
  1725  	"Annotations": true,
  1726  	"Labels":      true,
  1727  }
  1728  
  1729  // PersistentVolumeSpecValidationOptions contains the different settings for PeristentVolume validation
  1730  type PersistentVolumeSpecValidationOptions struct {
  1731  	// Allow users to modify the class of volume attributes
  1732  	EnableVolumeAttributesClass bool
  1733  }
  1734  
  1735  // ValidatePersistentVolumeName checks that a name is appropriate for a
  1736  // PersistentVolumeName object.
  1737  var ValidatePersistentVolumeName = apimachineryvalidation.NameIsDNSSubdomain
  1738  
  1739  var supportedAccessModes = sets.New(
  1740  	core.ReadWriteOnce,
  1741  	core.ReadOnlyMany,
  1742  	core.ReadWriteMany,
  1743  	core.ReadWriteOncePod)
  1744  
  1745  var supportedReclaimPolicy = sets.New(
  1746  	core.PersistentVolumeReclaimDelete,
  1747  	core.PersistentVolumeReclaimRecycle,
  1748  	core.PersistentVolumeReclaimRetain)
  1749  
  1750  var supportedVolumeModes = sets.New(core.PersistentVolumeBlock, core.PersistentVolumeFilesystem)
  1751  
  1752  func ValidationOptionsForPersistentVolume(pv, oldPv *core.PersistentVolume) PersistentVolumeSpecValidationOptions {
  1753  	opts := PersistentVolumeSpecValidationOptions{
  1754  		EnableVolumeAttributesClass: utilfeature.DefaultMutableFeatureGate.Enabled(features.VolumeAttributesClass),
  1755  	}
  1756  	if oldPv != nil && oldPv.Spec.VolumeAttributesClassName != nil {
  1757  		opts.EnableVolumeAttributesClass = true
  1758  	}
  1759  	return opts
  1760  }
  1761  
  1762  func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName string, validateInlinePersistentVolumeSpec bool, fldPath *field.Path, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
  1763  	allErrs := field.ErrorList{}
  1764  
  1765  	if validateInlinePersistentVolumeSpec {
  1766  		if pvSpec.ClaimRef != nil {
  1767  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("claimRef"), "may not be specified in the context of inline volumes"))
  1768  		}
  1769  		if len(pvSpec.Capacity) != 0 {
  1770  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("capacity"), "may not be specified in the context of inline volumes"))
  1771  		}
  1772  		if pvSpec.CSI == nil {
  1773  			allErrs = append(allErrs, field.Required(fldPath.Child("csi"), "has to be specified in the context of inline volumes"))
  1774  		}
  1775  	}
  1776  
  1777  	if len(pvSpec.AccessModes) == 0 {
  1778  		allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), ""))
  1779  	}
  1780  
  1781  	foundReadWriteOncePod, foundNonReadWriteOncePod := false, false
  1782  	for _, mode := range pvSpec.AccessModes {
  1783  		if !supportedAccessModes.Has(mode) {
  1784  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, sets.List(supportedAccessModes)))
  1785  		}
  1786  
  1787  		if mode == core.ReadWriteOncePod {
  1788  			foundReadWriteOncePod = true
  1789  		} else if supportedAccessModes.Has(mode) {
  1790  			foundNonReadWriteOncePod = true
  1791  		}
  1792  	}
  1793  	if foundReadWriteOncePod && foundNonReadWriteOncePod {
  1794  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("accessModes"), "may not use ReadWriteOncePod with other access modes"))
  1795  	}
  1796  
  1797  	if !validateInlinePersistentVolumeSpec {
  1798  		if len(pvSpec.Capacity) == 0 {
  1799  			allErrs = append(allErrs, field.Required(fldPath.Child("capacity"), ""))
  1800  		}
  1801  
  1802  		if _, ok := pvSpec.Capacity[core.ResourceStorage]; !ok || len(pvSpec.Capacity) > 1 {
  1803  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("capacity"), pvSpec.Capacity, []core.ResourceName{core.ResourceStorage}))
  1804  		}
  1805  		capPath := fldPath.Child("capacity")
  1806  		for r, qty := range pvSpec.Capacity {
  1807  			allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...)
  1808  			allErrs = append(allErrs, ValidatePositiveQuantityValue(qty, capPath.Key(string(r)))...)
  1809  		}
  1810  	}
  1811  
  1812  	if len(pvSpec.PersistentVolumeReclaimPolicy) > 0 {
  1813  		if validateInlinePersistentVolumeSpec {
  1814  			if pvSpec.PersistentVolumeReclaimPolicy != core.PersistentVolumeReclaimRetain {
  1815  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("persistentVolumeReclaimPolicy"), "may only be "+string(core.PersistentVolumeReclaimRetain)+" in the context of inline volumes"))
  1816  			}
  1817  		} else {
  1818  			if !supportedReclaimPolicy.Has(pvSpec.PersistentVolumeReclaimPolicy) {
  1819  				allErrs = append(allErrs, field.NotSupported(fldPath.Child("persistentVolumeReclaimPolicy"), pvSpec.PersistentVolumeReclaimPolicy, sets.List(supportedReclaimPolicy)))
  1820  			}
  1821  		}
  1822  	}
  1823  
  1824  	var nodeAffinitySpecified bool
  1825  	var errs field.ErrorList
  1826  	if pvSpec.NodeAffinity != nil {
  1827  		if validateInlinePersistentVolumeSpec {
  1828  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeAffinity"), "may not be specified in the context of inline volumes"))
  1829  		} else {
  1830  			nodeAffinitySpecified, errs = validateVolumeNodeAffinity(pvSpec.NodeAffinity, fldPath.Child("nodeAffinity"))
  1831  			allErrs = append(allErrs, errs...)
  1832  		}
  1833  	}
  1834  	numVolumes := 0
  1835  	if pvSpec.HostPath != nil {
  1836  		if numVolumes > 0 {
  1837  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostPath"), "may not specify more than 1 volume type"))
  1838  		} else {
  1839  			numVolumes++
  1840  			allErrs = append(allErrs, validateHostPathVolumeSource(pvSpec.HostPath, fldPath.Child("hostPath"))...)
  1841  		}
  1842  	}
  1843  	if pvSpec.GCEPersistentDisk != nil {
  1844  		if numVolumes > 0 {
  1845  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("gcePersistentDisk"), "may not specify more than 1 volume type"))
  1846  		} else {
  1847  			numVolumes++
  1848  			allErrs = append(allErrs, validateGCEPersistentDiskVolumeSource(pvSpec.GCEPersistentDisk, fldPath.Child("persistentDisk"))...)
  1849  		}
  1850  	}
  1851  	if pvSpec.AWSElasticBlockStore != nil {
  1852  		if numVolumes > 0 {
  1853  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("awsElasticBlockStore"), "may not specify more than 1 volume type"))
  1854  		} else {
  1855  			numVolumes++
  1856  			allErrs = append(allErrs, validateAWSElasticBlockStoreVolumeSource(pvSpec.AWSElasticBlockStore, fldPath.Child("awsElasticBlockStore"))...)
  1857  		}
  1858  	}
  1859  	if pvSpec.Glusterfs != nil {
  1860  		if numVolumes > 0 {
  1861  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("glusterfs"), "may not specify more than 1 volume type"))
  1862  		} else {
  1863  			numVolumes++
  1864  			allErrs = append(allErrs, validateGlusterfsPersistentVolumeSource(pvSpec.Glusterfs, fldPath.Child("glusterfs"))...)
  1865  		}
  1866  	}
  1867  	if pvSpec.Flocker != nil {
  1868  		if numVolumes > 0 {
  1869  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("flocker"), "may not specify more than 1 volume type"))
  1870  		} else {
  1871  			numVolumes++
  1872  			allErrs = append(allErrs, validateFlockerVolumeSource(pvSpec.Flocker, fldPath.Child("flocker"))...)
  1873  		}
  1874  	}
  1875  	if pvSpec.NFS != nil {
  1876  		if numVolumes > 0 {
  1877  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("nfs"), "may not specify more than 1 volume type"))
  1878  		} else {
  1879  			numVolumes++
  1880  			allErrs = append(allErrs, validateNFSVolumeSource(pvSpec.NFS, fldPath.Child("nfs"))...)
  1881  		}
  1882  	}
  1883  	if pvSpec.RBD != nil {
  1884  		if numVolumes > 0 {
  1885  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("rbd"), "may not specify more than 1 volume type"))
  1886  		} else {
  1887  			numVolumes++
  1888  			allErrs = append(allErrs, validateRBDPersistentVolumeSource(pvSpec.RBD, fldPath.Child("rbd"))...)
  1889  		}
  1890  	}
  1891  	if pvSpec.Quobyte != nil {
  1892  		if numVolumes > 0 {
  1893  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("quobyte"), "may not specify more than 1 volume type"))
  1894  		} else {
  1895  			numVolumes++
  1896  			allErrs = append(allErrs, validateQuobyteVolumeSource(pvSpec.Quobyte, fldPath.Child("quobyte"))...)
  1897  		}
  1898  	}
  1899  	if pvSpec.CephFS != nil {
  1900  		if numVolumes > 0 {
  1901  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("cephFS"), "may not specify more than 1 volume type"))
  1902  		} else {
  1903  			numVolumes++
  1904  			allErrs = append(allErrs, validateCephFSPersistentVolumeSource(pvSpec.CephFS, fldPath.Child("cephfs"))...)
  1905  		}
  1906  	}
  1907  	if pvSpec.ISCSI != nil {
  1908  		if numVolumes > 0 {
  1909  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("iscsi"), "may not specify more than 1 volume type"))
  1910  		} else {
  1911  			numVolumes++
  1912  			allErrs = append(allErrs, validateISCSIPersistentVolumeSource(pvSpec.ISCSI, pvName, fldPath.Child("iscsi"))...)
  1913  		}
  1914  	}
  1915  	if pvSpec.Cinder != nil {
  1916  		if numVolumes > 0 {
  1917  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("cinder"), "may not specify more than 1 volume type"))
  1918  		} else {
  1919  			numVolumes++
  1920  			allErrs = append(allErrs, validateCinderPersistentVolumeSource(pvSpec.Cinder, fldPath.Child("cinder"))...)
  1921  		}
  1922  	}
  1923  	if pvSpec.FC != nil {
  1924  		if numVolumes > 0 {
  1925  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("fc"), "may not specify more than 1 volume type"))
  1926  		} else {
  1927  			numVolumes++
  1928  			allErrs = append(allErrs, validateFCVolumeSource(pvSpec.FC, fldPath.Child("fc"))...)
  1929  		}
  1930  	}
  1931  	if pvSpec.FlexVolume != nil {
  1932  		numVolumes++
  1933  		allErrs = append(allErrs, validateFlexPersistentVolumeSource(pvSpec.FlexVolume, fldPath.Child("flexVolume"))...)
  1934  	}
  1935  	if pvSpec.AzureFile != nil {
  1936  		if numVolumes > 0 {
  1937  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureFile"), "may not specify more than 1 volume type"))
  1938  
  1939  		} else {
  1940  			numVolumes++
  1941  			allErrs = append(allErrs, validateAzureFilePV(pvSpec.AzureFile, fldPath.Child("azureFile"))...)
  1942  		}
  1943  	}
  1944  
  1945  	if pvSpec.VsphereVolume != nil {
  1946  		if numVolumes > 0 {
  1947  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("vsphereVolume"), "may not specify more than 1 volume type"))
  1948  		} else {
  1949  			numVolumes++
  1950  			allErrs = append(allErrs, validateVsphereVolumeSource(pvSpec.VsphereVolume, fldPath.Child("vsphereVolume"))...)
  1951  		}
  1952  	}
  1953  	if pvSpec.PhotonPersistentDisk != nil {
  1954  		if numVolumes > 0 {
  1955  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("photonPersistentDisk"), "may not specify more than 1 volume type"))
  1956  		} else {
  1957  			numVolumes++
  1958  			allErrs = append(allErrs, validatePhotonPersistentDiskVolumeSource(pvSpec.PhotonPersistentDisk, fldPath.Child("photonPersistentDisk"))...)
  1959  		}
  1960  	}
  1961  	if pvSpec.PortworxVolume != nil {
  1962  		if numVolumes > 0 {
  1963  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("portworxVolume"), "may not specify more than 1 volume type"))
  1964  		} else {
  1965  			numVolumes++
  1966  			allErrs = append(allErrs, validatePortworxVolumeSource(pvSpec.PortworxVolume, fldPath.Child("portworxVolume"))...)
  1967  		}
  1968  	}
  1969  	if pvSpec.AzureDisk != nil {
  1970  		if numVolumes > 0 {
  1971  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("azureDisk"), "may not specify more than 1 volume type"))
  1972  		} else {
  1973  			numVolumes++
  1974  			allErrs = append(allErrs, validateAzureDisk(pvSpec.AzureDisk, fldPath.Child("azureDisk"))...)
  1975  		}
  1976  	}
  1977  	if pvSpec.ScaleIO != nil {
  1978  		if numVolumes > 0 {
  1979  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("scaleIO"), "may not specify more than 1 volume type"))
  1980  		} else {
  1981  			numVolumes++
  1982  			allErrs = append(allErrs, validateScaleIOPersistentVolumeSource(pvSpec.ScaleIO, fldPath.Child("scaleIO"))...)
  1983  		}
  1984  	}
  1985  	if pvSpec.Local != nil {
  1986  		if numVolumes > 0 {
  1987  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("local"), "may not specify more than 1 volume type"))
  1988  		} else {
  1989  			numVolumes++
  1990  			allErrs = append(allErrs, validateLocalVolumeSource(pvSpec.Local, fldPath.Child("local"))...)
  1991  			// NodeAffinity is required
  1992  			if !nodeAffinitySpecified {
  1993  				allErrs = append(allErrs, field.Required(fldPath.Child("nodeAffinity"), "Local volume requires node affinity"))
  1994  			}
  1995  		}
  1996  	}
  1997  	if pvSpec.StorageOS != nil {
  1998  		if numVolumes > 0 {
  1999  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("storageos"), "may not specify more than 1 volume type"))
  2000  		} else {
  2001  			numVolumes++
  2002  			allErrs = append(allErrs, validateStorageOSPersistentVolumeSource(pvSpec.StorageOS, fldPath.Child("storageos"))...)
  2003  		}
  2004  	}
  2005  
  2006  	if pvSpec.CSI != nil {
  2007  		if numVolumes > 0 {
  2008  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("csi"), "may not specify more than 1 volume type"))
  2009  		} else {
  2010  			numVolumes++
  2011  			allErrs = append(allErrs, validateCSIPersistentVolumeSource(pvSpec.CSI, fldPath.Child("csi"))...)
  2012  		}
  2013  	}
  2014  
  2015  	if numVolumes == 0 {
  2016  		allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
  2017  	}
  2018  
  2019  	// do not allow hostPath mounts of '/' to have a 'recycle' reclaim policy
  2020  	if pvSpec.HostPath != nil && path.Clean(pvSpec.HostPath.Path) == "/" && pvSpec.PersistentVolumeReclaimPolicy == core.PersistentVolumeReclaimRecycle {
  2021  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("persistentVolumeReclaimPolicy"), "may not be 'recycle' for a hostPath mount of '/'"))
  2022  	}
  2023  
  2024  	if len(pvSpec.StorageClassName) > 0 {
  2025  		if validateInlinePersistentVolumeSpec {
  2026  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("storageClassName"), "may not be specified in the context of inline volumes"))
  2027  		} else {
  2028  			for _, msg := range ValidateClassName(pvSpec.StorageClassName, false) {
  2029  				allErrs = append(allErrs, field.Invalid(fldPath.Child("storageClassName"), pvSpec.StorageClassName, msg))
  2030  			}
  2031  		}
  2032  	}
  2033  	if pvSpec.VolumeMode != nil {
  2034  		if validateInlinePersistentVolumeSpec {
  2035  			if *pvSpec.VolumeMode != core.PersistentVolumeFilesystem {
  2036  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("volumeMode"), "may not specify volumeMode other than "+string(core.PersistentVolumeFilesystem)+" in the context of inline volumes"))
  2037  			}
  2038  		} else {
  2039  			if !supportedVolumeModes.Has(*pvSpec.VolumeMode) {
  2040  				allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumeMode"), *pvSpec.VolumeMode, sets.List(supportedVolumeModes)))
  2041  			}
  2042  		}
  2043  	}
  2044  	if pvSpec.VolumeAttributesClassName != nil && opts.EnableVolumeAttributesClass {
  2045  		if len(*pvSpec.VolumeAttributesClassName) == 0 {
  2046  			allErrs = append(allErrs, field.Required(fldPath.Child("volumeAttributesClassName"), "an empty string is disallowed"))
  2047  		} else {
  2048  			for _, msg := range ValidateClassName(*pvSpec.VolumeAttributesClassName, false) {
  2049  				allErrs = append(allErrs, field.Invalid(fldPath.Child("volumeAttributesClassName"), *pvSpec.VolumeAttributesClassName, msg))
  2050  			}
  2051  		}
  2052  		if pvSpec.CSI == nil {
  2053  			allErrs = append(allErrs, field.Required(fldPath.Child("csi"), "has to be specified when using volumeAttributesClassName"))
  2054  		}
  2055  	}
  2056  	return allErrs
  2057  }
  2058  
  2059  func ValidatePersistentVolume(pv *core.PersistentVolume, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
  2060  	metaPath := field.NewPath("metadata")
  2061  	allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName, metaPath)
  2062  	allErrs = append(allErrs, ValidatePersistentVolumeSpec(&pv.Spec, pv.ObjectMeta.Name, false, field.NewPath("spec"), opts)...)
  2063  	return allErrs
  2064  }
  2065  
  2066  // ValidatePersistentVolumeUpdate tests to see if the update is legal for an end user to make.
  2067  // newPv is updated with fields that cannot be changed.
  2068  func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
  2069  	allErrs := ValidatePersistentVolume(newPv, opts)
  2070  
  2071  	// if oldPV does not have ControllerExpandSecretRef then allow it to be set
  2072  	if (oldPv.Spec.CSI != nil && oldPv.Spec.CSI.ControllerExpandSecretRef == nil) &&
  2073  		(newPv.Spec.CSI != nil && newPv.Spec.CSI.ControllerExpandSecretRef != nil) {
  2074  		newPv = newPv.DeepCopy()
  2075  		newPv.Spec.CSI.ControllerExpandSecretRef = nil
  2076  	}
  2077  
  2078  	// PersistentVolumeSource should be immutable after creation.
  2079  	if !apiequality.Semantic.DeepEqual(newPv.Spec.PersistentVolumeSource, oldPv.Spec.PersistentVolumeSource) {
  2080  		pvcSourceDiff := cmp.Diff(oldPv.Spec.PersistentVolumeSource, newPv.Spec.PersistentVolumeSource)
  2081  		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "persistentvolumesource"), fmt.Sprintf("spec.persistentvolumesource is immutable after creation\n%v", pvcSourceDiff)))
  2082  	}
  2083  	allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...)
  2084  
  2085  	// Allow setting NodeAffinity if oldPv NodeAffinity was not set
  2086  	if oldPv.Spec.NodeAffinity != nil {
  2087  		allErrs = append(allErrs, validatePvNodeAffinity(newPv.Spec.NodeAffinity, oldPv.Spec.NodeAffinity, field.NewPath("nodeAffinity"))...)
  2088  	}
  2089  
  2090  	if !apiequality.Semantic.DeepEqual(oldPv.Spec.VolumeAttributesClassName, newPv.Spec.VolumeAttributesClassName) {
  2091  		if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeAttributesClass) {
  2092  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "volumeAttributesClassName"), "update is forbidden when the VolumeAttributesClass feature gate is disabled"))
  2093  		}
  2094  		if opts.EnableVolumeAttributesClass {
  2095  			if oldPv.Spec.VolumeAttributesClassName != nil && newPv.Spec.VolumeAttributesClassName == nil {
  2096  				allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "volumeAttributesClassName"), "update from non-nil value to nil is forbidden"))
  2097  			}
  2098  		}
  2099  	}
  2100  
  2101  	return allErrs
  2102  }
  2103  
  2104  // ValidatePersistentVolumeStatusUpdate tests to see if the status update is legal for an end user to make.
  2105  func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
  2106  	allErrs := ValidateObjectMetaUpdate(&newPv.ObjectMeta, &oldPv.ObjectMeta, field.NewPath("metadata"))
  2107  	if len(newPv.ResourceVersion) == 0 {
  2108  		allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
  2109  	}
  2110  	return allErrs
  2111  }
  2112  
  2113  type PersistentVolumeClaimSpecValidationOptions struct {
  2114  	// Allow users to recover from previously failing expansion operation
  2115  	EnableRecoverFromExpansionFailure bool
  2116  	// Allow to validate the label value of the label selector
  2117  	AllowInvalidLabelValueInSelector bool
  2118  	// Allow to validate the API group of the data source and data source reference
  2119  	AllowInvalidAPIGroupInDataSourceOrRef bool
  2120  	// Allow users to modify the class of volume attributes
  2121  	EnableVolumeAttributesClass bool
  2122  }
  2123  
  2124  func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolumeClaim) PersistentVolumeClaimSpecValidationOptions {
  2125  	opts := PersistentVolumeClaimSpecValidationOptions{
  2126  		EnableRecoverFromExpansionFailure: utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure),
  2127  		AllowInvalidLabelValueInSelector:  false,
  2128  		EnableVolumeAttributesClass:       utilfeature.DefaultFeatureGate.Enabled(features.VolumeAttributesClass),
  2129  	}
  2130  	if oldPvc == nil {
  2131  		// If there's no old PVC, use the options based solely on feature enablement
  2132  		return opts
  2133  	}
  2134  
  2135  	// If the old object had an invalid API group in the data source or data source reference, continue to allow it in the new object
  2136  	opts.AllowInvalidAPIGroupInDataSourceOrRef = allowInvalidAPIGroupInDataSourceOrRef(&oldPvc.Spec)
  2137  
  2138  	if oldPvc.Spec.VolumeAttributesClassName != nil {
  2139  		// If the old object had a volume attributes class, continue to validate it in the new object.
  2140  		opts.EnableVolumeAttributesClass = true
  2141  	}
  2142  
  2143  	labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
  2144  		AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
  2145  	}
  2146  	if len(unversionedvalidation.ValidateLabelSelector(oldPvc.Spec.Selector, labelSelectorValidationOpts, nil)) > 0 {
  2147  		// If the old object had an invalid label selector, continue to allow it in the new object
  2148  		opts.AllowInvalidLabelValueInSelector = true
  2149  	}
  2150  
  2151  	if helper.ClaimContainsAllocatedResources(oldPvc) ||
  2152  		helper.ClaimContainsAllocatedResourceStatus(oldPvc) {
  2153  		opts.EnableRecoverFromExpansionFailure = true
  2154  	}
  2155  	return opts
  2156  }
  2157  
  2158  func ValidationOptionsForPersistentVolumeClaimTemplate(claimTemplate, oldClaimTemplate *core.PersistentVolumeClaimTemplate) PersistentVolumeClaimSpecValidationOptions {
  2159  	opts := PersistentVolumeClaimSpecValidationOptions{
  2160  		AllowInvalidLabelValueInSelector: false,
  2161  		EnableVolumeAttributesClass:      utilfeature.DefaultFeatureGate.Enabled(features.VolumeAttributesClass),
  2162  	}
  2163  	if oldClaimTemplate == nil {
  2164  		// If there's no old PVC template, use the options based solely on feature enablement
  2165  		return opts
  2166  	}
  2167  	labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
  2168  		AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
  2169  	}
  2170  	if len(unversionedvalidation.ValidateLabelSelector(oldClaimTemplate.Spec.Selector, labelSelectorValidationOpts, nil)) > 0 {
  2171  		// If the old object had an invalid label selector, continue to allow it in the new object
  2172  		opts.AllowInvalidLabelValueInSelector = true
  2173  	}
  2174  	return opts
  2175  }
  2176  
  2177  // allowInvalidAPIGroupInDataSourceOrRef returns true if the spec contains a data source or data source reference with an API group
  2178  func allowInvalidAPIGroupInDataSourceOrRef(spec *core.PersistentVolumeClaimSpec) bool {
  2179  	if spec.DataSource != nil && spec.DataSource.APIGroup != nil {
  2180  		return true
  2181  	}
  2182  	if spec.DataSourceRef != nil && spec.DataSourceRef.APIGroup != nil {
  2183  		return true
  2184  	}
  2185  	return false
  2186  }
  2187  
  2188  // ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
  2189  func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
  2190  	allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName, field.NewPath("metadata"))
  2191  	allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&pvc.Spec, field.NewPath("spec"), opts)...)
  2192  	return allErrs
  2193  }
  2194  
  2195  // validateDataSource validates a DataSource/DataSourceRef in a PersistentVolumeClaimSpec
  2196  func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *field.Path, allowInvalidAPIGroupInDataSourceOrRef bool) field.ErrorList {
  2197  	allErrs := field.ErrorList{}
  2198  
  2199  	if len(dataSource.Name) == 0 {
  2200  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  2201  	}
  2202  	if len(dataSource.Kind) == 0 {
  2203  		allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
  2204  	}
  2205  	apiGroup := ""
  2206  	if dataSource.APIGroup != nil {
  2207  		apiGroup = *dataSource.APIGroup
  2208  	}
  2209  	if len(apiGroup) == 0 && dataSource.Kind != "PersistentVolumeClaim" {
  2210  		allErrs = append(allErrs, field.Invalid(fldPath, dataSource.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
  2211  	}
  2212  	if len(apiGroup) > 0 && !allowInvalidAPIGroupInDataSourceOrRef {
  2213  		for _, errString := range validation.IsDNS1123Subdomain(apiGroup) {
  2214  			allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), apiGroup, errString))
  2215  		}
  2216  	}
  2217  
  2218  	return allErrs
  2219  }
  2220  
  2221  // validateDataSourceRef validates a DataSourceRef in a PersistentVolumeClaimSpec
  2222  func validateDataSourceRef(dataSourceRef *core.TypedObjectReference, fldPath *field.Path, allowInvalidAPIGroupInDataSourceOrRef bool) field.ErrorList {
  2223  	allErrs := field.ErrorList{}
  2224  
  2225  	if len(dataSourceRef.Name) == 0 {
  2226  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  2227  	}
  2228  	if len(dataSourceRef.Kind) == 0 {
  2229  		allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
  2230  	}
  2231  	apiGroup := ""
  2232  	if dataSourceRef.APIGroup != nil {
  2233  		apiGroup = *dataSourceRef.APIGroup
  2234  	}
  2235  	if len(apiGroup) == 0 && dataSourceRef.Kind != "PersistentVolumeClaim" {
  2236  		allErrs = append(allErrs, field.Invalid(fldPath, dataSourceRef.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
  2237  	}
  2238  	if len(apiGroup) > 0 && !allowInvalidAPIGroupInDataSourceOrRef {
  2239  		for _, errString := range validation.IsDNS1123Subdomain(apiGroup) {
  2240  			allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), apiGroup, errString))
  2241  		}
  2242  	}
  2243  
  2244  	if dataSourceRef.Namespace != nil && len(*dataSourceRef.Namespace) > 0 {
  2245  		for _, msg := range ValidateNameFunc(ValidateNamespaceName)(*dataSourceRef.Namespace, false) {
  2246  			allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), *dataSourceRef.Namespace, msg))
  2247  		}
  2248  	}
  2249  
  2250  	return allErrs
  2251  }
  2252  
  2253  // ValidatePersistentVolumeClaimSpec validates a PersistentVolumeClaimSpec
  2254  func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fldPath *field.Path, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
  2255  	allErrs := field.ErrorList{}
  2256  	if len(spec.AccessModes) == 0 {
  2257  		allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), "at least 1 access mode is required"))
  2258  	}
  2259  	if spec.Selector != nil {
  2260  		labelSelectorValidationOpts := unversionedvalidation.LabelSelectorValidationOptions{
  2261  			AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector,
  2262  		}
  2263  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, labelSelectorValidationOpts, fldPath.Child("selector"))...)
  2264  	}
  2265  
  2266  	foundReadWriteOncePod, foundNonReadWriteOncePod := false, false
  2267  	for _, mode := range spec.AccessModes {
  2268  		if !supportedAccessModes.Has(mode) {
  2269  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("accessModes"), mode, sets.List(supportedAccessModes)))
  2270  		}
  2271  
  2272  		if mode == core.ReadWriteOncePod {
  2273  			foundReadWriteOncePod = true
  2274  		} else if supportedAccessModes.Has(mode) {
  2275  			foundNonReadWriteOncePod = true
  2276  		}
  2277  	}
  2278  	if foundReadWriteOncePod && foundNonReadWriteOncePod {
  2279  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("accessModes"), "may not use ReadWriteOncePod with other access modes"))
  2280  	}
  2281  
  2282  	storageValue, ok := spec.Resources.Requests[core.ResourceStorage]
  2283  	if !ok {
  2284  		allErrs = append(allErrs, field.Required(fldPath.Child("resources").Key(string(core.ResourceStorage)), ""))
  2285  	} else if errs := ValidatePositiveQuantityValue(storageValue, fldPath.Child("resources").Key(string(core.ResourceStorage))); len(errs) > 0 {
  2286  		allErrs = append(allErrs, errs...)
  2287  	} else {
  2288  		allErrs = append(allErrs, ValidateResourceQuantityValue(core.ResourceStorage, storageValue, fldPath.Child("resources").Key(string(core.ResourceStorage)))...)
  2289  	}
  2290  
  2291  	if spec.StorageClassName != nil && len(*spec.StorageClassName) > 0 {
  2292  		for _, msg := range ValidateClassName(*spec.StorageClassName, false) {
  2293  			allErrs = append(allErrs, field.Invalid(fldPath.Child("storageClassName"), *spec.StorageClassName, msg))
  2294  		}
  2295  	}
  2296  	if spec.VolumeMode != nil && !supportedVolumeModes.Has(*spec.VolumeMode) {
  2297  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumeMode"), *spec.VolumeMode, sets.List(supportedVolumeModes)))
  2298  	}
  2299  
  2300  	if spec.DataSource != nil {
  2301  		allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"), opts.AllowInvalidAPIGroupInDataSourceOrRef)...)
  2302  	}
  2303  	if spec.DataSourceRef != nil {
  2304  		allErrs = append(allErrs, validateDataSourceRef(spec.DataSourceRef, fldPath.Child("dataSourceRef"), opts.AllowInvalidAPIGroupInDataSourceOrRef)...)
  2305  	}
  2306  	if spec.DataSourceRef != nil && spec.DataSourceRef.Namespace != nil && len(*spec.DataSourceRef.Namespace) > 0 {
  2307  		if spec.DataSource != nil {
  2308  			allErrs = append(allErrs, field.Invalid(fldPath, fldPath.Child("dataSource"),
  2309  				"may not be specified when dataSourceRef.namespace is specified"))
  2310  		}
  2311  	} else if spec.DataSource != nil && spec.DataSourceRef != nil {
  2312  		if !isDataSourceEqualDataSourceRef(spec.DataSource, spec.DataSourceRef) {
  2313  			allErrs = append(allErrs, field.Invalid(fldPath, fldPath.Child("dataSource"),
  2314  				"must match dataSourceRef"))
  2315  		}
  2316  	}
  2317  	if spec.VolumeAttributesClassName != nil && len(*spec.VolumeAttributesClassName) > 0 && opts.EnableVolumeAttributesClass {
  2318  		for _, msg := range ValidateClassName(*spec.VolumeAttributesClassName, false) {
  2319  			allErrs = append(allErrs, field.Invalid(fldPath.Child("volumeAttributesClassName"), *spec.VolumeAttributesClassName, msg))
  2320  		}
  2321  	}
  2322  
  2323  	return allErrs
  2324  }
  2325  
  2326  func isDataSourceEqualDataSourceRef(dataSource *core.TypedLocalObjectReference, dataSourceRef *core.TypedObjectReference) bool {
  2327  	return reflect.DeepEqual(dataSource.APIGroup, dataSourceRef.APIGroup) && dataSource.Kind == dataSourceRef.Kind && dataSource.Name == dataSourceRef.Name
  2328  }
  2329  
  2330  // ValidatePersistentVolumeClaimUpdate validates an update to a PersistentVolumeClaim
  2331  func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
  2332  	allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
  2333  	allErrs = append(allErrs, ValidatePersistentVolumeClaim(newPvc, opts)...)
  2334  	newPvcClone := newPvc.DeepCopy()
  2335  	oldPvcClone := oldPvc.DeepCopy()
  2336  
  2337  	// PVController needs to update PVC.Spec w/ VolumeName.
  2338  	// Claims are immutable in order to enforce quota, range limits, etc. without gaming the system.
  2339  	if len(oldPvc.Spec.VolumeName) == 0 {
  2340  		// volumeName changes are allowed once.
  2341  		oldPvcClone.Spec.VolumeName = newPvcClone.Spec.VolumeName // +k8s:verify-mutation:reason=clone
  2342  	}
  2343  
  2344  	if validateStorageClassUpgradeFromAnnotation(oldPvcClone.Annotations, newPvcClone.Annotations,
  2345  		oldPvcClone.Spec.StorageClassName, newPvcClone.Spec.StorageClassName) {
  2346  		newPvcClone.Spec.StorageClassName = nil
  2347  		metav1.SetMetaDataAnnotation(&newPvcClone.ObjectMeta, core.BetaStorageClassAnnotation, oldPvcClone.Annotations[core.BetaStorageClassAnnotation])
  2348  	} else {
  2349  		// storageclass annotation should be immutable after creation
  2350  		// TODO: remove Beta when no longer needed
  2351  		allErrs = append(allErrs, ValidateImmutableAnnotation(newPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], oldPvc.ObjectMeta.Annotations[v1.BetaStorageClassAnnotation], v1.BetaStorageClassAnnotation, field.NewPath("metadata"))...)
  2352  
  2353  		// If update from annotation to attribute failed we can attempt try to validate update from nil value.
  2354  		if validateStorageClassUpgradeFromNil(oldPvc.Annotations, oldPvc.Spec.StorageClassName, newPvc.Spec.StorageClassName, opts) {
  2355  			newPvcClone.Spec.StorageClassName = oldPvcClone.Spec.StorageClassName // +k8s:verify-mutation:reason=clone
  2356  		}
  2357  		// TODO: add a specific error with a hint that storage class name can not be changed
  2358  		// (instead of letting spec comparison below return generic field forbidden error)
  2359  	}
  2360  
  2361  	// lets make sure storage values are same.
  2362  	if newPvc.Status.Phase == core.ClaimBound && newPvcClone.Spec.Resources.Requests != nil {
  2363  		newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"] // +k8s:verify-mutation:reason=clone
  2364  	}
  2365  	// lets make sure volume attributes class name is same.
  2366  	newPvcClone.Spec.VolumeAttributesClassName = oldPvcClone.Spec.VolumeAttributesClassName // +k8s:verify-mutation:reason=clone
  2367  
  2368  	oldSize := oldPvc.Spec.Resources.Requests["storage"]
  2369  	newSize := newPvc.Spec.Resources.Requests["storage"]
  2370  	statusSize := oldPvc.Status.Capacity["storage"]
  2371  
  2372  	if !apiequality.Semantic.DeepEqual(newPvcClone.Spec, oldPvcClone.Spec) {
  2373  		specDiff := cmp.Diff(oldPvcClone.Spec, newPvcClone.Spec)
  2374  		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), fmt.Sprintf("spec is immutable after creation except resources.requests and volumeAttributesClassName for bound claims\n%v", specDiff)))
  2375  	}
  2376  	if newSize.Cmp(oldSize) < 0 {
  2377  		if !opts.EnableRecoverFromExpansionFailure {
  2378  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than previous value"))
  2379  		} else {
  2380  			// This validation permits reducing pvc requested size up to capacity recorded in pvc.status
  2381  			// so that users can recover from volume expansion failure, but Kubernetes does not actually
  2382  			// support volume shrinking
  2383  			if newSize.Cmp(statusSize) <= 0 {
  2384  				allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than status.capacity"))
  2385  			}
  2386  		}
  2387  	}
  2388  
  2389  	allErrs = append(allErrs, ValidateImmutableField(newPvc.Spec.VolumeMode, oldPvc.Spec.VolumeMode, field.NewPath("volumeMode"))...)
  2390  
  2391  	if !apiequality.Semantic.DeepEqual(oldPvc.Spec.VolumeAttributesClassName, newPvc.Spec.VolumeAttributesClassName) {
  2392  		if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeAttributesClass) {
  2393  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "volumeAttributesClassName"), "update is forbidden when the VolumeAttributesClass feature gate is disabled"))
  2394  		}
  2395  		if opts.EnableVolumeAttributesClass {
  2396  			if oldPvc.Spec.VolumeAttributesClassName != nil {
  2397  				if newPvc.Spec.VolumeAttributesClassName == nil {
  2398  					allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "volumeAttributesClassName"), "update from non-nil value to nil is forbidden"))
  2399  				} else if len(*newPvc.Spec.VolumeAttributesClassName) == 0 {
  2400  					allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "volumeAttributesClassName"), "update from non-nil value to an empty string is forbidden"))
  2401  				}
  2402  			}
  2403  		}
  2404  	}
  2405  
  2406  	return allErrs
  2407  }
  2408  
  2409  // Provide an upgrade path from PVC with storage class specified in beta
  2410  // annotation to storage class specified in attribute. We allow update of
  2411  // StorageClassName only if following four conditions are met at the same time:
  2412  // 1. The old pvc's StorageClassAnnotation is set
  2413  // 2. The old pvc's StorageClassName is not set
  2414  // 3. The new pvc's StorageClassName is set and equal to the old value in annotation
  2415  // 4. If the new pvc's StorageClassAnnotation is set,it must be equal to the old pv/pvc's StorageClassAnnotation
  2416  func validateStorageClassUpgradeFromAnnotation(oldAnnotations, newAnnotations map[string]string, oldScName, newScName *string) bool {
  2417  	oldSc, oldAnnotationExist := oldAnnotations[core.BetaStorageClassAnnotation]
  2418  	newScInAnnotation, newAnnotationExist := newAnnotations[core.BetaStorageClassAnnotation]
  2419  	return oldAnnotationExist /* condition 1 */ &&
  2420  		oldScName == nil /* condition 2*/ &&
  2421  		(newScName != nil && *newScName == oldSc) /* condition 3 */ &&
  2422  		(!newAnnotationExist || newScInAnnotation == oldSc) /* condition 4 */
  2423  }
  2424  
  2425  // Provide an upgrade path from PVC with nil storage class. We allow update of
  2426  // StorageClassName only if following four conditions are met at the same time:
  2427  // 1. The new pvc's StorageClassName is not nil
  2428  // 2. The old pvc's StorageClassName is nil
  2429  // 3. The old pvc either does not have beta annotation set, or the beta annotation matches new pvc's StorageClassName
  2430  func validateStorageClassUpgradeFromNil(oldAnnotations map[string]string, oldScName, newScName *string, opts PersistentVolumeClaimSpecValidationOptions) bool {
  2431  	oldAnnotation, oldAnnotationExist := oldAnnotations[core.BetaStorageClassAnnotation]
  2432  	return newScName != nil /* condition 1 */ &&
  2433  		oldScName == nil /* condition 2 */ &&
  2434  		(!oldAnnotationExist || *newScName == oldAnnotation) /* condition 3 */
  2435  }
  2436  
  2437  func validatePersistentVolumeClaimResourceKey(value string, fldPath *field.Path) field.ErrorList {
  2438  	allErrs := field.ErrorList{}
  2439  	for _, msg := range validation.IsQualifiedName(value) {
  2440  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  2441  	}
  2442  	if len(allErrs) != 0 {
  2443  		return allErrs
  2444  	}
  2445  	// For native resource names such as - either unprefixed names or with kubernetes.io prefix,
  2446  	// only allowed value is storage
  2447  	if helper.IsNativeResource(core.ResourceName(value)) {
  2448  		if core.ResourceName(value) != core.ResourceStorage {
  2449  			return append(allErrs, field.NotSupported(fldPath, value, []core.ResourceName{core.ResourceStorage}))
  2450  		}
  2451  	}
  2452  	return allErrs
  2453  }
  2454  
  2455  var resizeStatusSet = sets.New(core.PersistentVolumeClaimControllerResizeInProgress,
  2456  	core.PersistentVolumeClaimControllerResizeFailed,
  2457  	core.PersistentVolumeClaimNodeResizePending,
  2458  	core.PersistentVolumeClaimNodeResizeInProgress,
  2459  	core.PersistentVolumeClaimNodeResizeFailed)
  2460  
  2461  // ValidatePersistentVolumeClaimStatusUpdate validates an update to status of a PersistentVolumeClaim
  2462  func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, validationOpts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
  2463  	allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
  2464  	if len(newPvc.ResourceVersion) == 0 {
  2465  		allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
  2466  	}
  2467  	if len(newPvc.Spec.AccessModes) == 0 {
  2468  		allErrs = append(allErrs, field.Required(field.NewPath("Spec", "accessModes"), ""))
  2469  	}
  2470  
  2471  	capPath := field.NewPath("status", "capacity")
  2472  	for r, qty := range newPvc.Status.Capacity {
  2473  		allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...)
  2474  	}
  2475  	if validationOpts.EnableRecoverFromExpansionFailure {
  2476  		resizeStatusPath := field.NewPath("status", "allocatedResourceStatus")
  2477  		if newPvc.Status.AllocatedResourceStatuses != nil {
  2478  			resizeStatus := newPvc.Status.AllocatedResourceStatuses
  2479  			for k, v := range resizeStatus {
  2480  				if errs := validatePersistentVolumeClaimResourceKey(k.String(), resizeStatusPath); len(errs) > 0 {
  2481  					allErrs = append(allErrs, errs...)
  2482  				}
  2483  				if !resizeStatusSet.Has(v) {
  2484  					allErrs = append(allErrs, field.NotSupported(resizeStatusPath, k, sets.List(resizeStatusSet)))
  2485  					continue
  2486  				}
  2487  			}
  2488  		}
  2489  		allocPath := field.NewPath("status", "allocatedResources")
  2490  		for r, qty := range newPvc.Status.AllocatedResources {
  2491  			if errs := validatePersistentVolumeClaimResourceKey(r.String(), allocPath); len(errs) > 0 {
  2492  				allErrs = append(allErrs, errs...)
  2493  				continue
  2494  			}
  2495  
  2496  			if errs := validateBasicResource(qty, allocPath.Key(string(r))); len(errs) > 0 {
  2497  				allErrs = append(allErrs, errs...)
  2498  			} else {
  2499  				allErrs = append(allErrs, ValidateResourceQuantityValue(core.ResourceStorage, qty, allocPath.Key(string(r)))...)
  2500  			}
  2501  		}
  2502  	}
  2503  	return allErrs
  2504  }
  2505  
  2506  var supportedPortProtocols = sets.New(
  2507  	core.ProtocolTCP,
  2508  	core.ProtocolUDP,
  2509  	core.ProtocolSCTP)
  2510  
  2511  func validateContainerPorts(ports []core.ContainerPort, fldPath *field.Path) field.ErrorList {
  2512  	allErrs := field.ErrorList{}
  2513  
  2514  	allNames := sets.Set[string]{}
  2515  	for i, port := range ports {
  2516  		idxPath := fldPath.Index(i)
  2517  		if len(port.Name) > 0 {
  2518  			if msgs := validation.IsValidPortName(port.Name); len(msgs) != 0 {
  2519  				for i = range msgs {
  2520  					allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), port.Name, msgs[i]))
  2521  				}
  2522  			} else if allNames.Has(port.Name) {
  2523  				allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), port.Name))
  2524  			} else {
  2525  				allNames.Insert(port.Name)
  2526  			}
  2527  		}
  2528  		if port.ContainerPort == 0 {
  2529  			allErrs = append(allErrs, field.Required(idxPath.Child("containerPort"), ""))
  2530  		} else {
  2531  			for _, msg := range validation.IsValidPortNum(int(port.ContainerPort)) {
  2532  				allErrs = append(allErrs, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, msg))
  2533  			}
  2534  		}
  2535  		if port.HostPort != 0 {
  2536  			for _, msg := range validation.IsValidPortNum(int(port.HostPort)) {
  2537  				allErrs = append(allErrs, field.Invalid(idxPath.Child("hostPort"), port.HostPort, msg))
  2538  			}
  2539  		}
  2540  		if len(port.Protocol) == 0 {
  2541  			allErrs = append(allErrs, field.Required(idxPath.Child("protocol"), ""))
  2542  		} else if !supportedPortProtocols.Has(port.Protocol) {
  2543  			allErrs = append(allErrs, field.NotSupported(idxPath.Child("protocol"), port.Protocol, sets.List(supportedPortProtocols)))
  2544  		}
  2545  	}
  2546  	return allErrs
  2547  }
  2548  
  2549  // ValidateEnv validates env vars
  2550  func ValidateEnv(vars []core.EnvVar, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  2551  	allErrs := field.ErrorList{}
  2552  
  2553  	for i, ev := range vars {
  2554  		idxPath := fldPath.Index(i)
  2555  		if len(ev.Name) == 0 {
  2556  			allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
  2557  		} else {
  2558  			for _, msg := range validation.IsEnvVarName(ev.Name) {
  2559  				allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, msg))
  2560  			}
  2561  		}
  2562  		allErrs = append(allErrs, validateEnvVarValueFrom(ev, idxPath.Child("valueFrom"), opts)...)
  2563  	}
  2564  	return allErrs
  2565  }
  2566  
  2567  var validEnvDownwardAPIFieldPathExpressions = sets.New(
  2568  	"metadata.name",
  2569  	"metadata.namespace",
  2570  	"metadata.uid",
  2571  	"spec.nodeName",
  2572  	"spec.serviceAccountName",
  2573  	"status.hostIP",
  2574  	"status.hostIPs",
  2575  	"status.podIP",
  2576  	"status.podIPs",
  2577  )
  2578  
  2579  var validContainerResourceFieldPathExpressions = sets.New(
  2580  	"limits.cpu",
  2581  	"limits.memory",
  2582  	"limits.ephemeral-storage",
  2583  	"requests.cpu",
  2584  	"requests.memory",
  2585  	"requests.ephemeral-storage",
  2586  )
  2587  
  2588  var validContainerResourceFieldPathPrefixesWithDownwardAPIHugePages = sets.New(hugepagesRequestsPrefixDownwardAPI, hugepagesLimitsPrefixDownwardAPI)
  2589  
  2590  const hugepagesRequestsPrefixDownwardAPI string = `requests.hugepages-`
  2591  const hugepagesLimitsPrefixDownwardAPI string = `limits.hugepages-`
  2592  
  2593  func validateEnvVarValueFrom(ev core.EnvVar, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  2594  	allErrs := field.ErrorList{}
  2595  
  2596  	if ev.ValueFrom == nil {
  2597  		return allErrs
  2598  	}
  2599  
  2600  	numSources := 0
  2601  
  2602  	if ev.ValueFrom.FieldRef != nil {
  2603  		numSources++
  2604  		allErrs = append(allErrs, validateObjectFieldSelector(ev.ValueFrom.FieldRef, &validEnvDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
  2605  		allErrs = append(allErrs, validateDownwardAPIHostIPs(ev.ValueFrom.FieldRef, fldPath.Child("fieldRef"), opts)...)
  2606  	}
  2607  	if ev.ValueFrom.ResourceFieldRef != nil {
  2608  		numSources++
  2609  		localValidContainerResourceFieldPathPrefixes := validContainerResourceFieldPathPrefixesWithDownwardAPIHugePages
  2610  		allErrs = append(allErrs, validateContainerResourceFieldSelector(ev.ValueFrom.ResourceFieldRef, &validContainerResourceFieldPathExpressions, &localValidContainerResourceFieldPathPrefixes, fldPath.Child("resourceFieldRef"), false)...)
  2611  	}
  2612  	if ev.ValueFrom.ConfigMapKeyRef != nil {
  2613  		numSources++
  2614  		allErrs = append(allErrs, validateConfigMapKeySelector(ev.ValueFrom.ConfigMapKeyRef, fldPath.Child("configMapKeyRef"))...)
  2615  	}
  2616  	if ev.ValueFrom.SecretKeyRef != nil {
  2617  		numSources++
  2618  		allErrs = append(allErrs, validateSecretKeySelector(ev.ValueFrom.SecretKeyRef, fldPath.Child("secretKeyRef"))...)
  2619  	}
  2620  
  2621  	if numSources == 0 {
  2622  		allErrs = append(allErrs, field.Invalid(fldPath, "", "must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`"))
  2623  	} else if len(ev.Value) != 0 {
  2624  		if numSources != 0 {
  2625  			allErrs = append(allErrs, field.Invalid(fldPath, "", "may not be specified when `value` is not empty"))
  2626  		}
  2627  	} else if numSources > 1 {
  2628  		allErrs = append(allErrs, field.Invalid(fldPath, "", "may not have more than one field specified at a time"))
  2629  	}
  2630  
  2631  	return allErrs
  2632  }
  2633  
  2634  func validateObjectFieldSelector(fs *core.ObjectFieldSelector, expressions *sets.Set[string], fldPath *field.Path) field.ErrorList {
  2635  	allErrs := field.ErrorList{}
  2636  
  2637  	if len(fs.APIVersion) == 0 {
  2638  		allErrs = append(allErrs, field.Required(fldPath.Child("apiVersion"), ""))
  2639  		return allErrs
  2640  	}
  2641  	if len(fs.FieldPath) == 0 {
  2642  		allErrs = append(allErrs, field.Required(fldPath.Child("fieldPath"), ""))
  2643  		return allErrs
  2644  	}
  2645  
  2646  	internalFieldPath, _, err := podshelper.ConvertDownwardAPIFieldLabel(fs.APIVersion, fs.FieldPath, "")
  2647  	if err != nil {
  2648  		allErrs = append(allErrs, field.Invalid(fldPath.Child("fieldPath"), fs.FieldPath, fmt.Sprintf("error converting fieldPath: %v", err)))
  2649  		return allErrs
  2650  	}
  2651  
  2652  	if path, subscript, ok := fieldpath.SplitMaybeSubscriptedPath(internalFieldPath); ok {
  2653  		switch path {
  2654  		case "metadata.annotations":
  2655  			allErrs = append(allErrs, ValidateQualifiedName(strings.ToLower(subscript), fldPath)...)
  2656  		case "metadata.labels":
  2657  			allErrs = append(allErrs, ValidateQualifiedName(subscript, fldPath)...)
  2658  		default:
  2659  			allErrs = append(allErrs, field.Invalid(fldPath, path, "does not support subscript"))
  2660  		}
  2661  	} else if !expressions.Has(path) {
  2662  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("fieldPath"), path, sets.List(*expressions)))
  2663  		return allErrs
  2664  	}
  2665  
  2666  	return allErrs
  2667  }
  2668  
  2669  func validateDownwardAPIHostIPs(fieldSel *core.ObjectFieldSelector, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  2670  	allErrs := field.ErrorList{}
  2671  	if !opts.AllowHostIPsField {
  2672  		if fieldSel.FieldPath == "status.hostIPs" {
  2673  			allErrs = append(allErrs, field.Forbidden(fldPath, "may not be set when feature gate 'PodHostIPs' is not enabled"))
  2674  		}
  2675  	}
  2676  	return allErrs
  2677  }
  2678  
  2679  func validateContainerResourceFieldSelector(fs *core.ResourceFieldSelector, expressions *sets.Set[string], prefixes *sets.Set[string], fldPath *field.Path, volume bool) field.ErrorList {
  2680  	allErrs := field.ErrorList{}
  2681  
  2682  	if volume && len(fs.ContainerName) == 0 {
  2683  		allErrs = append(allErrs, field.Required(fldPath.Child("containerName"), ""))
  2684  	} else if len(fs.Resource) == 0 {
  2685  		allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
  2686  	} else if !expressions.Has(fs.Resource) {
  2687  		// check if the prefix is present
  2688  		foundPrefix := false
  2689  		if prefixes != nil {
  2690  			for _, prefix := range sets.List(*prefixes) {
  2691  				if strings.HasPrefix(fs.Resource, prefix) {
  2692  					foundPrefix = true
  2693  				}
  2694  			}
  2695  		}
  2696  		if !foundPrefix {
  2697  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("resource"), fs.Resource, sets.List(*expressions)))
  2698  		}
  2699  	}
  2700  	allErrs = append(allErrs, validateContainerResourceDivisor(fs.Resource, fs.Divisor, fldPath)...)
  2701  	return allErrs
  2702  }
  2703  
  2704  func ValidateEnvFrom(vars []core.EnvFromSource, fldPath *field.Path) field.ErrorList {
  2705  	allErrs := field.ErrorList{}
  2706  	for i, ev := range vars {
  2707  		idxPath := fldPath.Index(i)
  2708  		if len(ev.Prefix) > 0 {
  2709  			for _, msg := range validation.IsEnvVarName(ev.Prefix) {
  2710  				allErrs = append(allErrs, field.Invalid(idxPath.Child("prefix"), ev.Prefix, msg))
  2711  			}
  2712  		}
  2713  
  2714  		numSources := 0
  2715  		if ev.ConfigMapRef != nil {
  2716  			numSources++
  2717  			allErrs = append(allErrs, validateConfigMapEnvSource(ev.ConfigMapRef, idxPath.Child("configMapRef"))...)
  2718  		}
  2719  		if ev.SecretRef != nil {
  2720  			numSources++
  2721  			allErrs = append(allErrs, validateSecretEnvSource(ev.SecretRef, idxPath.Child("secretRef"))...)
  2722  		}
  2723  
  2724  		if numSources == 0 {
  2725  			allErrs = append(allErrs, field.Invalid(fldPath, "", "must specify one of: `configMapRef` or `secretRef`"))
  2726  		} else if numSources > 1 {
  2727  			allErrs = append(allErrs, field.Invalid(fldPath, "", "may not have more than one field specified at a time"))
  2728  		}
  2729  	}
  2730  	return allErrs
  2731  }
  2732  
  2733  func validateConfigMapEnvSource(configMapSource *core.ConfigMapEnvSource, fldPath *field.Path) field.ErrorList {
  2734  	allErrs := field.ErrorList{}
  2735  	if len(configMapSource.Name) == 0 {
  2736  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  2737  	} else {
  2738  		for _, msg := range ValidateConfigMapName(configMapSource.Name, true) {
  2739  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), configMapSource.Name, msg))
  2740  		}
  2741  	}
  2742  	return allErrs
  2743  }
  2744  
  2745  func validateSecretEnvSource(secretSource *core.SecretEnvSource, fldPath *field.Path) field.ErrorList {
  2746  	allErrs := field.ErrorList{}
  2747  	if len(secretSource.Name) == 0 {
  2748  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  2749  	} else {
  2750  		for _, msg := range ValidateSecretName(secretSource.Name, true) {
  2751  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), secretSource.Name, msg))
  2752  		}
  2753  	}
  2754  	return allErrs
  2755  }
  2756  
  2757  var validContainerResourceDivisorForCPU = sets.New("1m", "1")
  2758  var validContainerResourceDivisorForMemory = sets.New(
  2759  	"1",
  2760  	"1k", "1M", "1G", "1T", "1P", "1E",
  2761  	"1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
  2762  var validContainerResourceDivisorForHugePages = sets.New(
  2763  	"1",
  2764  	"1k", "1M", "1G", "1T", "1P", "1E",
  2765  	"1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
  2766  var validContainerResourceDivisorForEphemeralStorage = sets.New(
  2767  	"1",
  2768  	"1k", "1M", "1G", "1T", "1P", "1E",
  2769  	"1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
  2770  
  2771  func validateContainerResourceDivisor(rName string, divisor resource.Quantity, fldPath *field.Path) field.ErrorList {
  2772  	allErrs := field.ErrorList{}
  2773  	unsetDivisor := resource.Quantity{}
  2774  	if unsetDivisor.Cmp(divisor) == 0 {
  2775  		return allErrs
  2776  	}
  2777  	switch rName {
  2778  	case "limits.cpu", "requests.cpu":
  2779  		if !validContainerResourceDivisorForCPU.Has(divisor.String()) {
  2780  			allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1m and 1 are supported with the cpu resource"))
  2781  		}
  2782  	case "limits.memory", "requests.memory":
  2783  		if !validContainerResourceDivisorForMemory.Has(divisor.String()) {
  2784  			allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the memory resource"))
  2785  		}
  2786  	case "limits.ephemeral-storage", "requests.ephemeral-storage":
  2787  		if !validContainerResourceDivisorForEphemeralStorage.Has(divisor.String()) {
  2788  			allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the local ephemeral storage resource"))
  2789  		}
  2790  	}
  2791  	if strings.HasPrefix(rName, hugepagesRequestsPrefixDownwardAPI) || strings.HasPrefix(rName, hugepagesLimitsPrefixDownwardAPI) {
  2792  		if !validContainerResourceDivisorForHugePages.Has(divisor.String()) {
  2793  			allErrs = append(allErrs, field.Invalid(fldPath.Child("divisor"), rName, "only divisor's values 1, 1k, 1M, 1G, 1T, 1P, 1E, 1Ki, 1Mi, 1Gi, 1Ti, 1Pi, 1Ei are supported with the hugepages resource"))
  2794  		}
  2795  	}
  2796  	return allErrs
  2797  }
  2798  
  2799  func validateConfigMapKeySelector(s *core.ConfigMapKeySelector, fldPath *field.Path) field.ErrorList {
  2800  	allErrs := field.ErrorList{}
  2801  
  2802  	nameFn := ValidateNameFunc(ValidateSecretName)
  2803  	for _, msg := range nameFn(s.Name, false) {
  2804  		allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), s.Name, msg))
  2805  	}
  2806  	if len(s.Key) == 0 {
  2807  		allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
  2808  	} else {
  2809  		for _, msg := range validation.IsConfigMapKey(s.Key) {
  2810  			allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), s.Key, msg))
  2811  		}
  2812  	}
  2813  
  2814  	return allErrs
  2815  }
  2816  
  2817  func validateSecretKeySelector(s *core.SecretKeySelector, fldPath *field.Path) field.ErrorList {
  2818  	allErrs := field.ErrorList{}
  2819  
  2820  	nameFn := ValidateNameFunc(ValidateSecretName)
  2821  	for _, msg := range nameFn(s.Name, false) {
  2822  		allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), s.Name, msg))
  2823  	}
  2824  	if len(s.Key) == 0 {
  2825  		allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
  2826  	} else {
  2827  		for _, msg := range validation.IsConfigMapKey(s.Key) {
  2828  			allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), s.Key, msg))
  2829  		}
  2830  	}
  2831  
  2832  	return allErrs
  2833  }
  2834  
  2835  func GetVolumeMountMap(mounts []core.VolumeMount) map[string]string {
  2836  	volmounts := make(map[string]string)
  2837  
  2838  	for _, mnt := range mounts {
  2839  		volmounts[mnt.Name] = mnt.MountPath
  2840  	}
  2841  
  2842  	return volmounts
  2843  }
  2844  
  2845  func GetVolumeDeviceMap(devices []core.VolumeDevice) map[string]string {
  2846  	volDevices := make(map[string]string)
  2847  
  2848  	for _, dev := range devices {
  2849  		volDevices[dev.Name] = dev.DevicePath
  2850  	}
  2851  
  2852  	return volDevices
  2853  }
  2854  
  2855  func ValidateVolumeMounts(mounts []core.VolumeMount, voldevices map[string]string, volumes map[string]core.VolumeSource, container *core.Container, fldPath *field.Path) field.ErrorList {
  2856  	allErrs := field.ErrorList{}
  2857  	mountpoints := sets.New[string]()
  2858  
  2859  	for i, mnt := range mounts {
  2860  		idxPath := fldPath.Index(i)
  2861  		if len(mnt.Name) == 0 {
  2862  			allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
  2863  		}
  2864  		if !IsMatchedVolume(mnt.Name, volumes) {
  2865  			allErrs = append(allErrs, field.NotFound(idxPath.Child("name"), mnt.Name))
  2866  		}
  2867  		if len(mnt.MountPath) == 0 {
  2868  			allErrs = append(allErrs, field.Required(idxPath.Child("mountPath"), ""))
  2869  		}
  2870  		if mountpoints.Has(mnt.MountPath) {
  2871  			allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique"))
  2872  		}
  2873  		mountpoints.Insert(mnt.MountPath)
  2874  
  2875  		// check for overlap with VolumeDevice
  2876  		if mountNameAlreadyExists(mnt.Name, voldevices) {
  2877  			allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), mnt.Name, "must not already exist in volumeDevices"))
  2878  		}
  2879  		if mountPathAlreadyExists(mnt.MountPath, voldevices) {
  2880  			allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must not already exist as a path in volumeDevices"))
  2881  		}
  2882  
  2883  		if len(mnt.SubPath) > 0 {
  2884  			allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...)
  2885  		}
  2886  
  2887  		if len(mnt.SubPathExpr) > 0 {
  2888  			if len(mnt.SubPath) > 0 {
  2889  				allErrs = append(allErrs, field.Invalid(idxPath.Child("subPathExpr"), mnt.SubPathExpr, "subPathExpr and subPath are mutually exclusive"))
  2890  			}
  2891  
  2892  			allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPathExpr, fldPath.Child("subPathExpr"))...)
  2893  		}
  2894  
  2895  		if mnt.MountPropagation != nil {
  2896  			allErrs = append(allErrs, validateMountPropagation(mnt.MountPropagation, container, fldPath.Child("mountPropagation"))...)
  2897  		}
  2898  	}
  2899  	return allErrs
  2900  }
  2901  
  2902  func ValidateVolumeDevices(devices []core.VolumeDevice, volmounts map[string]string, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList {
  2903  	allErrs := field.ErrorList{}
  2904  	devicepath := sets.New[string]()
  2905  	devicename := sets.New[string]()
  2906  
  2907  	for i, dev := range devices {
  2908  		idxPath := fldPath.Index(i)
  2909  		devName := dev.Name
  2910  		devPath := dev.DevicePath
  2911  		didMatch, isPVC := isMatchedDevice(devName, volumes)
  2912  		if len(devName) == 0 {
  2913  			allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
  2914  		}
  2915  		if devicename.Has(devName) {
  2916  			allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "must be unique"))
  2917  		}
  2918  		// Must be based on PersistentVolumeClaim (PVC reference or generic ephemeral inline volume)
  2919  		if didMatch && !isPVC {
  2920  			allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "can only use volume source type of PersistentVolumeClaim or Ephemeral for block mode"))
  2921  		}
  2922  		if !didMatch {
  2923  			allErrs = append(allErrs, field.NotFound(idxPath.Child("name"), devName))
  2924  		}
  2925  		if len(devPath) == 0 {
  2926  			allErrs = append(allErrs, field.Required(idxPath.Child("devicePath"), ""))
  2927  		}
  2928  		if devicepath.Has(devPath) {
  2929  			allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "must be unique"))
  2930  		}
  2931  		if len(devPath) > 0 && len(validatePathNoBacksteps(devPath, fldPath.Child("devicePath"))) > 0 {
  2932  			allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "can not contain backsteps ('..')"))
  2933  		} else {
  2934  			devicepath.Insert(devPath)
  2935  		}
  2936  		// check for overlap with VolumeMount
  2937  		if deviceNameAlreadyExists(devName, volmounts) {
  2938  			allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), devName, "must not already exist in volumeMounts"))
  2939  		}
  2940  		if devicePathAlreadyExists(devPath, volmounts) {
  2941  			allErrs = append(allErrs, field.Invalid(idxPath.Child("devicePath"), devPath, "must not already exist as a path in volumeMounts"))
  2942  		}
  2943  		if len(devName) > 0 {
  2944  			devicename.Insert(devName)
  2945  		}
  2946  	}
  2947  	return allErrs
  2948  }
  2949  
  2950  func validatePodResourceClaims(podMeta *metav1.ObjectMeta, claims []core.PodResourceClaim, fldPath *field.Path) field.ErrorList {
  2951  	var allErrs field.ErrorList
  2952  	podClaimNames := sets.New[string]()
  2953  	for i, claim := range claims {
  2954  		allErrs = append(allErrs, validatePodResourceClaim(podMeta, claim, &podClaimNames, fldPath.Index(i))...)
  2955  	}
  2956  	return allErrs
  2957  }
  2958  
  2959  // gatherPodResourceClaimNames returns a set of all non-empty
  2960  // PodResourceClaim.Name values. Validation that those names are valid is
  2961  // handled by validatePodResourceClaims.
  2962  func gatherPodResourceClaimNames(claims []core.PodResourceClaim) sets.Set[string] {
  2963  	podClaimNames := sets.Set[string]{}
  2964  	for _, claim := range claims {
  2965  		if claim.Name != "" {
  2966  			podClaimNames.Insert(claim.Name)
  2967  		}
  2968  	}
  2969  	return podClaimNames
  2970  }
  2971  
  2972  func validatePodResourceClaim(podMeta *metav1.ObjectMeta, claim core.PodResourceClaim, podClaimNames *sets.Set[string], fldPath *field.Path) field.ErrorList {
  2973  	var allErrs field.ErrorList
  2974  	if claim.Name == "" {
  2975  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  2976  	} else if podClaimNames.Has(claim.Name) {
  2977  		allErrs = append(allErrs, field.Duplicate(fldPath.Child("name"), claim.Name))
  2978  	} else {
  2979  		nameErrs := ValidateDNS1123Label(claim.Name, fldPath.Child("name"))
  2980  		if len(nameErrs) > 0 {
  2981  			allErrs = append(allErrs, nameErrs...)
  2982  		} else if podMeta != nil && claim.Source.ResourceClaimTemplateName != nil {
  2983  			claimName := podMeta.Name + "-" + claim.Name
  2984  			for _, detail := range ValidateResourceClaimName(claimName, false) {
  2985  				allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), claimName, "final ResourceClaim name: "+detail))
  2986  			}
  2987  		}
  2988  		podClaimNames.Insert(claim.Name)
  2989  	}
  2990  	allErrs = append(allErrs, validatePodResourceClaimSource(claim.Source, fldPath.Child("source"))...)
  2991  
  2992  	return allErrs
  2993  }
  2994  
  2995  func validatePodResourceClaimSource(claimSource core.ClaimSource, fldPath *field.Path) field.ErrorList {
  2996  	var allErrs field.ErrorList
  2997  	if claimSource.ResourceClaimName != nil && claimSource.ResourceClaimTemplateName != nil {
  2998  		allErrs = append(allErrs, field.Invalid(fldPath, claimSource, "at most one of `resourceClaimName` or `resourceClaimTemplateName` may be specified"))
  2999  	}
  3000  	if claimSource.ResourceClaimName == nil && claimSource.ResourceClaimTemplateName == nil {
  3001  		allErrs = append(allErrs, field.Invalid(fldPath, claimSource, "must specify one of: `resourceClaimName`, `resourceClaimTemplateName`"))
  3002  	}
  3003  	if claimSource.ResourceClaimName != nil {
  3004  		for _, detail := range ValidateResourceClaimName(*claimSource.ResourceClaimName, false) {
  3005  			allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceClaimName"), *claimSource.ResourceClaimName, detail))
  3006  		}
  3007  	}
  3008  	if claimSource.ResourceClaimTemplateName != nil {
  3009  		for _, detail := range ValidateResourceClaimTemplateName(*claimSource.ResourceClaimTemplateName, false) {
  3010  			allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceClaimTemplateName"), *claimSource.ResourceClaimTemplateName, detail))
  3011  		}
  3012  	}
  3013  	return allErrs
  3014  }
  3015  
  3016  func validateLivenessProbe(probe *core.Probe, gracePeriod int64, fldPath *field.Path) field.ErrorList {
  3017  	allErrs := field.ErrorList{}
  3018  
  3019  	if probe == nil {
  3020  		return allErrs
  3021  	}
  3022  	allErrs = append(allErrs, validateProbe(probe, gracePeriod, fldPath)...)
  3023  	if probe.SuccessThreshold != 1 {
  3024  		allErrs = append(allErrs, field.Invalid(fldPath.Child("successThreshold"), probe.SuccessThreshold, "must be 1"))
  3025  	}
  3026  	return allErrs
  3027  }
  3028  
  3029  func validateReadinessProbe(probe *core.Probe, gracePeriod int64, fldPath *field.Path) field.ErrorList {
  3030  	allErrs := field.ErrorList{}
  3031  
  3032  	if probe == nil {
  3033  		return allErrs
  3034  	}
  3035  	allErrs = append(allErrs, validateProbe(probe, gracePeriod, fldPath)...)
  3036  	if probe.TerminationGracePeriodSeconds != nil {
  3037  		allErrs = append(allErrs, field.Invalid(fldPath.Child("terminationGracePeriodSeconds"), probe.TerminationGracePeriodSeconds, "must not be set for readinessProbes"))
  3038  	}
  3039  	return allErrs
  3040  }
  3041  
  3042  func validateStartupProbe(probe *core.Probe, gracePeriod int64, fldPath *field.Path) field.ErrorList {
  3043  	allErrs := field.ErrorList{}
  3044  
  3045  	if probe == nil {
  3046  		return allErrs
  3047  	}
  3048  	allErrs = append(allErrs, validateProbe(probe, gracePeriod, fldPath)...)
  3049  	if probe.SuccessThreshold != 1 {
  3050  		allErrs = append(allErrs, field.Invalid(fldPath.Child("successThreshold"), probe.SuccessThreshold, "must be 1"))
  3051  	}
  3052  	return allErrs
  3053  }
  3054  
  3055  func validateProbe(probe *core.Probe, gracePeriod int64, fldPath *field.Path) field.ErrorList {
  3056  	allErrs := field.ErrorList{}
  3057  
  3058  	if probe == nil {
  3059  		return allErrs
  3060  	}
  3061  	allErrs = append(allErrs, validateHandler(handlerFromProbe(&probe.ProbeHandler), gracePeriod, fldPath)...)
  3062  
  3063  	allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.InitialDelaySeconds), fldPath.Child("initialDelaySeconds"))...)
  3064  	allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.TimeoutSeconds), fldPath.Child("timeoutSeconds"))...)
  3065  	allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.PeriodSeconds), fldPath.Child("periodSeconds"))...)
  3066  	allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.SuccessThreshold), fldPath.Child("successThreshold"))...)
  3067  	allErrs = append(allErrs, ValidateNonnegativeField(int64(probe.FailureThreshold), fldPath.Child("failureThreshold"))...)
  3068  	if probe.TerminationGracePeriodSeconds != nil && *probe.TerminationGracePeriodSeconds <= 0 {
  3069  		allErrs = append(allErrs, field.Invalid(fldPath.Child("terminationGracePeriodSeconds"), *probe.TerminationGracePeriodSeconds, "must be greater than 0"))
  3070  	}
  3071  	return allErrs
  3072  }
  3073  
  3074  func validateInitContainerRestartPolicy(restartPolicy *core.ContainerRestartPolicy, fldPath *field.Path) field.ErrorList {
  3075  	var allErrors field.ErrorList
  3076  
  3077  	if restartPolicy == nil {
  3078  		return allErrors
  3079  	}
  3080  	switch *restartPolicy {
  3081  	case core.ContainerRestartPolicyAlways:
  3082  		break
  3083  	default:
  3084  		validValues := []core.ContainerRestartPolicy{core.ContainerRestartPolicyAlways}
  3085  		allErrors = append(allErrors, field.NotSupported(fldPath, *restartPolicy, validValues))
  3086  	}
  3087  
  3088  	return allErrors
  3089  }
  3090  
  3091  type commonHandler struct {
  3092  	Exec      *core.ExecAction
  3093  	HTTPGet   *core.HTTPGetAction
  3094  	TCPSocket *core.TCPSocketAction
  3095  	GRPC      *core.GRPCAction
  3096  	Sleep     *core.SleepAction
  3097  }
  3098  
  3099  func handlerFromProbe(ph *core.ProbeHandler) commonHandler {
  3100  	return commonHandler{
  3101  		Exec:      ph.Exec,
  3102  		HTTPGet:   ph.HTTPGet,
  3103  		TCPSocket: ph.TCPSocket,
  3104  		GRPC:      ph.GRPC,
  3105  	}
  3106  }
  3107  
  3108  func handlerFromLifecycle(lh *core.LifecycleHandler) commonHandler {
  3109  	return commonHandler{
  3110  		Exec:      lh.Exec,
  3111  		HTTPGet:   lh.HTTPGet,
  3112  		TCPSocket: lh.TCPSocket,
  3113  		Sleep:     lh.Sleep,
  3114  	}
  3115  }
  3116  
  3117  func validateSleepAction(sleep *core.SleepAction, gracePeriod int64, fldPath *field.Path) field.ErrorList {
  3118  	allErrors := field.ErrorList{}
  3119  	if sleep.Seconds <= 0 || sleep.Seconds > gracePeriod {
  3120  		invalidStr := fmt.Sprintf("must be greater than 0 and less than terminationGracePeriodSeconds (%d)", gracePeriod)
  3121  		allErrors = append(allErrors, field.Invalid(fldPath, sleep.Seconds, invalidStr))
  3122  	}
  3123  	return allErrors
  3124  }
  3125  
  3126  func validateClientIPAffinityConfig(config *core.SessionAffinityConfig, fldPath *field.Path) field.ErrorList {
  3127  	allErrs := field.ErrorList{}
  3128  	if config == nil {
  3129  		allErrs = append(allErrs, field.Required(fldPath, fmt.Sprintf("when session affinity type is %s", core.ServiceAffinityClientIP)))
  3130  		return allErrs
  3131  	}
  3132  	if config.ClientIP == nil {
  3133  		allErrs = append(allErrs, field.Required(fldPath.Child("clientIP"), fmt.Sprintf("when session affinity type is %s", core.ServiceAffinityClientIP)))
  3134  		return allErrs
  3135  	}
  3136  	if config.ClientIP.TimeoutSeconds == nil {
  3137  		allErrs = append(allErrs, field.Required(fldPath.Child("clientIP").Child("timeoutSeconds"), fmt.Sprintf("when session affinity type is %s", core.ServiceAffinityClientIP)))
  3138  		return allErrs
  3139  	}
  3140  	allErrs = append(allErrs, validateAffinityTimeout(config.ClientIP.TimeoutSeconds, fldPath.Child("clientIP").Child("timeoutSeconds"))...)
  3141  
  3142  	return allErrs
  3143  }
  3144  
  3145  func validateAffinityTimeout(timeout *int32, fldPath *field.Path) field.ErrorList {
  3146  	allErrs := field.ErrorList{}
  3147  	if *timeout <= 0 || *timeout > core.MaxClientIPServiceAffinitySeconds {
  3148  		allErrs = append(allErrs, field.Invalid(fldPath, timeout, fmt.Sprintf("must be greater than 0 and less than %d", core.MaxClientIPServiceAffinitySeconds)))
  3149  	}
  3150  	return allErrs
  3151  }
  3152  
  3153  // AccumulateUniqueHostPorts extracts each HostPort of each Container,
  3154  // accumulating the results and returning an error if any ports conflict.
  3155  func AccumulateUniqueHostPorts(containers []core.Container, accumulator *sets.Set[string], fldPath *field.Path) field.ErrorList {
  3156  	allErrs := field.ErrorList{}
  3157  
  3158  	for ci, ctr := range containers {
  3159  		idxPath := fldPath.Index(ci)
  3160  		portsPath := idxPath.Child("ports")
  3161  		for pi := range ctr.Ports {
  3162  			idxPath := portsPath.Index(pi)
  3163  			port := ctr.Ports[pi].HostPort
  3164  			if port == 0 {
  3165  				continue
  3166  			}
  3167  			str := fmt.Sprintf("%s/%s/%d", ctr.Ports[pi].Protocol, ctr.Ports[pi].HostIP, port)
  3168  			if accumulator.Has(str) {
  3169  				allErrs = append(allErrs, field.Duplicate(idxPath.Child("hostPort"), str))
  3170  			} else {
  3171  				accumulator.Insert(str)
  3172  			}
  3173  		}
  3174  	}
  3175  	return allErrs
  3176  }
  3177  
  3178  // checkHostPortConflicts checks for colliding Port.HostPort values across
  3179  // a slice of containers.
  3180  func checkHostPortConflicts(containers []core.Container, fldPath *field.Path) field.ErrorList {
  3181  	allPorts := sets.Set[string]{}
  3182  	return AccumulateUniqueHostPorts(containers, &allPorts, fldPath)
  3183  }
  3184  
  3185  func validateExecAction(exec *core.ExecAction, fldPath *field.Path) field.ErrorList {
  3186  	allErrors := field.ErrorList{}
  3187  	if len(exec.Command) == 0 {
  3188  		allErrors = append(allErrors, field.Required(fldPath.Child("command"), ""))
  3189  	}
  3190  	return allErrors
  3191  }
  3192  
  3193  var supportedHTTPSchemes = sets.New(core.URISchemeHTTP, core.URISchemeHTTPS)
  3194  
  3195  func validateHTTPGetAction(http *core.HTTPGetAction, fldPath *field.Path) field.ErrorList {
  3196  	allErrors := field.ErrorList{}
  3197  	if len(http.Path) == 0 {
  3198  		allErrors = append(allErrors, field.Required(fldPath.Child("path"), ""))
  3199  	}
  3200  	allErrors = append(allErrors, ValidatePortNumOrName(http.Port, fldPath.Child("port"))...)
  3201  	if !supportedHTTPSchemes.Has(http.Scheme) {
  3202  		allErrors = append(allErrors, field.NotSupported(fldPath.Child("scheme"), http.Scheme, sets.List(supportedHTTPSchemes)))
  3203  	}
  3204  	for _, header := range http.HTTPHeaders {
  3205  		for _, msg := range validation.IsHTTPHeaderName(header.Name) {
  3206  			allErrors = append(allErrors, field.Invalid(fldPath.Child("httpHeaders"), header.Name, msg))
  3207  		}
  3208  	}
  3209  	return allErrors
  3210  }
  3211  
  3212  func ValidatePortNumOrName(port intstr.IntOrString, fldPath *field.Path) field.ErrorList {
  3213  	allErrs := field.ErrorList{}
  3214  	if port.Type == intstr.Int {
  3215  		for _, msg := range validation.IsValidPortNum(port.IntValue()) {
  3216  			allErrs = append(allErrs, field.Invalid(fldPath, port.IntValue(), msg))
  3217  		}
  3218  	} else if port.Type == intstr.String {
  3219  		for _, msg := range validation.IsValidPortName(port.StrVal) {
  3220  			allErrs = append(allErrs, field.Invalid(fldPath, port.StrVal, msg))
  3221  		}
  3222  	} else {
  3223  		allErrs = append(allErrs, field.InternalError(fldPath, fmt.Errorf("unknown type: %v", port.Type)))
  3224  	}
  3225  	return allErrs
  3226  }
  3227  
  3228  func validateTCPSocketAction(tcp *core.TCPSocketAction, fldPath *field.Path) field.ErrorList {
  3229  	return ValidatePortNumOrName(tcp.Port, fldPath.Child("port"))
  3230  }
  3231  func validateGRPCAction(grpc *core.GRPCAction, fldPath *field.Path) field.ErrorList {
  3232  	return ValidatePortNumOrName(intstr.FromInt32(grpc.Port), fldPath.Child("port"))
  3233  }
  3234  func validateHandler(handler commonHandler, gracePeriod int64, fldPath *field.Path) field.ErrorList {
  3235  	numHandlers := 0
  3236  	allErrors := field.ErrorList{}
  3237  	if handler.Exec != nil {
  3238  		if numHandlers > 0 {
  3239  			allErrors = append(allErrors, field.Forbidden(fldPath.Child("exec"), "may not specify more than 1 handler type"))
  3240  		} else {
  3241  			numHandlers++
  3242  			allErrors = append(allErrors, validateExecAction(handler.Exec, fldPath.Child("exec"))...)
  3243  		}
  3244  	}
  3245  	if handler.HTTPGet != nil {
  3246  		if numHandlers > 0 {
  3247  			allErrors = append(allErrors, field.Forbidden(fldPath.Child("httpGet"), "may not specify more than 1 handler type"))
  3248  		} else {
  3249  			numHandlers++
  3250  			allErrors = append(allErrors, validateHTTPGetAction(handler.HTTPGet, fldPath.Child("httpGet"))...)
  3251  		}
  3252  	}
  3253  	if handler.TCPSocket != nil {
  3254  		if numHandlers > 0 {
  3255  			allErrors = append(allErrors, field.Forbidden(fldPath.Child("tcpSocket"), "may not specify more than 1 handler type"))
  3256  		} else {
  3257  			numHandlers++
  3258  			allErrors = append(allErrors, validateTCPSocketAction(handler.TCPSocket, fldPath.Child("tcpSocket"))...)
  3259  		}
  3260  	}
  3261  	if handler.GRPC != nil {
  3262  		if numHandlers > 0 {
  3263  			allErrors = append(allErrors, field.Forbidden(fldPath.Child("grpc"), "may not specify more than 1 handler type"))
  3264  		} else {
  3265  			numHandlers++
  3266  			allErrors = append(allErrors, validateGRPCAction(handler.GRPC, fldPath.Child("grpc"))...)
  3267  		}
  3268  	}
  3269  	if handler.Sleep != nil {
  3270  		if numHandlers > 0 {
  3271  			allErrors = append(allErrors, field.Forbidden(fldPath.Child("sleep"), "may not specify more than 1 handler type"))
  3272  		} else {
  3273  			numHandlers++
  3274  			allErrors = append(allErrors, validateSleepAction(handler.Sleep, gracePeriod, fldPath.Child("sleep"))...)
  3275  		}
  3276  	}
  3277  	if numHandlers == 0 {
  3278  		allErrors = append(allErrors, field.Required(fldPath, "must specify a handler type"))
  3279  	}
  3280  	return allErrors
  3281  }
  3282  
  3283  func validateLifecycle(lifecycle *core.Lifecycle, gracePeriod int64, fldPath *field.Path) field.ErrorList {
  3284  	allErrs := field.ErrorList{}
  3285  	if lifecycle.PostStart != nil {
  3286  		allErrs = append(allErrs, validateHandler(handlerFromLifecycle(lifecycle.PostStart), gracePeriod, fldPath.Child("postStart"))...)
  3287  	}
  3288  	if lifecycle.PreStop != nil {
  3289  		allErrs = append(allErrs, validateHandler(handlerFromLifecycle(lifecycle.PreStop), gracePeriod, fldPath.Child("preStop"))...)
  3290  	}
  3291  	return allErrs
  3292  }
  3293  
  3294  var supportedPullPolicies = sets.New(
  3295  	core.PullAlways,
  3296  	core.PullIfNotPresent,
  3297  	core.PullNever)
  3298  
  3299  func validatePullPolicy(policy core.PullPolicy, fldPath *field.Path) field.ErrorList {
  3300  	allErrors := field.ErrorList{}
  3301  
  3302  	switch policy {
  3303  	case core.PullAlways, core.PullIfNotPresent, core.PullNever:
  3304  		break
  3305  	case "":
  3306  		allErrors = append(allErrors, field.Required(fldPath, ""))
  3307  	default:
  3308  		allErrors = append(allErrors, field.NotSupported(fldPath, policy, sets.List(supportedPullPolicies)))
  3309  	}
  3310  
  3311  	return allErrors
  3312  }
  3313  
  3314  var supportedResizeResources = sets.New(core.ResourceCPU, core.ResourceMemory)
  3315  var supportedResizePolicies = sets.New(core.NotRequired, core.RestartContainer)
  3316  
  3317  func validateResizePolicy(policyList []core.ContainerResizePolicy, fldPath *field.Path, podRestartPolicy *core.RestartPolicy) field.ErrorList {
  3318  	allErrors := field.ErrorList{}
  3319  
  3320  	// validate that resource name is not repeated, supported resource names and policy values are specified
  3321  	resources := make(map[core.ResourceName]bool)
  3322  	for i, p := range policyList {
  3323  		if _, found := resources[p.ResourceName]; found {
  3324  			allErrors = append(allErrors, field.Duplicate(fldPath.Index(i), p.ResourceName))
  3325  		}
  3326  		resources[p.ResourceName] = true
  3327  		switch p.ResourceName {
  3328  		case core.ResourceCPU, core.ResourceMemory:
  3329  		case "":
  3330  			allErrors = append(allErrors, field.Required(fldPath, ""))
  3331  		default:
  3332  			allErrors = append(allErrors, field.NotSupported(fldPath, p.ResourceName, sets.List(supportedResizeResources)))
  3333  		}
  3334  		switch p.RestartPolicy {
  3335  		case core.NotRequired, core.RestartContainer:
  3336  		case "":
  3337  			allErrors = append(allErrors, field.Required(fldPath, ""))
  3338  		default:
  3339  			allErrors = append(allErrors, field.NotSupported(fldPath, p.RestartPolicy, sets.List(supportedResizePolicies)))
  3340  		}
  3341  
  3342  		if *podRestartPolicy == core.RestartPolicyNever && p.RestartPolicy != core.NotRequired {
  3343  			allErrors = append(allErrors, field.Invalid(fldPath, p.RestartPolicy, "must be 'NotRequired' when `restartPolicy` is 'Never'"))
  3344  		}
  3345  	}
  3346  	return allErrors
  3347  }
  3348  
  3349  // validateEphemeralContainers is called by pod spec and template validation to validate the list of ephemeral containers.
  3350  // Note that this is called for pod template even though ephemeral containers aren't allowed in pod templates.
  3351  func validateEphemeralContainers(ephemeralContainers []core.EphemeralContainer, containers, initContainers []core.Container, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy) field.ErrorList {
  3352  	var allErrs field.ErrorList
  3353  
  3354  	if len(ephemeralContainers) == 0 {
  3355  		return allErrs
  3356  	}
  3357  
  3358  	otherNames, allNames := sets.Set[string]{}, sets.Set[string]{}
  3359  	for _, c := range containers {
  3360  		otherNames.Insert(c.Name)
  3361  		allNames.Insert(c.Name)
  3362  	}
  3363  	for _, c := range initContainers {
  3364  		otherNames.Insert(c.Name)
  3365  		allNames.Insert(c.Name)
  3366  	}
  3367  
  3368  	for i, ec := range ephemeralContainers {
  3369  		idxPath := fldPath.Index(i)
  3370  
  3371  		c := (*core.Container)(&ec.EphemeralContainerCommon)
  3372  		allErrs = append(allErrs, validateContainerCommon(c, volumes, podClaimNames, idxPath, opts, podRestartPolicy)...)
  3373  		// Ephemeral containers don't need looser constraints for pod templates, so it's convenient to apply both validations
  3374  		// here where we've already converted EphemeralContainerCommon to Container.
  3375  		allErrs = append(allErrs, validateContainerOnlyForPod(c, idxPath)...)
  3376  
  3377  		// Ephemeral containers must have a name unique across all container types.
  3378  		if allNames.Has(ec.Name) {
  3379  			allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), ec.Name))
  3380  		} else {
  3381  			allNames.Insert(ec.Name)
  3382  		}
  3383  
  3384  		// The target container name must exist and be non-ephemeral.
  3385  		if ec.TargetContainerName != "" && !otherNames.Has(ec.TargetContainerName) {
  3386  			allErrs = append(allErrs, field.NotFound(idxPath.Child("targetContainerName"), ec.TargetContainerName))
  3387  		}
  3388  
  3389  		// Ephemeral containers should not be relied upon for fundamental pod services, so fields such as
  3390  		// Lifecycle, probes, resources and ports should be disallowed. This is implemented as a list
  3391  		// of allowed fields so that new fields will be given consideration prior to inclusion in ephemeral containers.
  3392  		allErrs = append(allErrs, validateFieldAllowList(ec.EphemeralContainerCommon, allowedEphemeralContainerFields, "cannot be set for an Ephemeral Container", idxPath)...)
  3393  
  3394  		// VolumeMount subpaths have the potential to leak resources since they're implemented with bind mounts
  3395  		// that aren't cleaned up until the pod exits. Since they also imply that the container is being used
  3396  		// as part of the workload, they're disallowed entirely.
  3397  		for i, vm := range ec.VolumeMounts {
  3398  			if vm.SubPath != "" {
  3399  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("volumeMounts").Index(i).Child("subPath"), "cannot be set for an Ephemeral Container"))
  3400  			}
  3401  			if vm.SubPathExpr != "" {
  3402  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("volumeMounts").Index(i).Child("subPathExpr"), "cannot be set for an Ephemeral Container"))
  3403  			}
  3404  		}
  3405  	}
  3406  
  3407  	return allErrs
  3408  }
  3409  
  3410  // ValidateFieldAcceptList checks that only allowed fields are set.
  3411  // The value must be a struct (not a pointer to a struct!).
  3412  func validateFieldAllowList(value interface{}, allowedFields map[string]bool, errorText string, fldPath *field.Path) field.ErrorList {
  3413  	var allErrs field.ErrorList
  3414  
  3415  	reflectType, reflectValue := reflect.TypeOf(value), reflect.ValueOf(value)
  3416  	for i := 0; i < reflectType.NumField(); i++ {
  3417  		f := reflectType.Field(i)
  3418  		if allowedFields[f.Name] {
  3419  			continue
  3420  		}
  3421  
  3422  		// Compare the value of this field to its zero value to determine if it has been set
  3423  		if !reflect.DeepEqual(reflectValue.Field(i).Interface(), reflect.Zero(f.Type).Interface()) {
  3424  			r, n := utf8.DecodeRuneInString(f.Name)
  3425  			lcName := string(unicode.ToLower(r)) + f.Name[n:]
  3426  			allErrs = append(allErrs, field.Forbidden(fldPath.Child(lcName), errorText))
  3427  		}
  3428  	}
  3429  
  3430  	return allErrs
  3431  }
  3432  
  3433  // validateInitContainers is called by pod spec and template validation to validate the list of init containers
  3434  func validateInitContainers(containers []core.Container, regularContainers []core.Container, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], gracePeriod int64, fldPath *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy) field.ErrorList {
  3435  	var allErrs field.ErrorList
  3436  
  3437  	allNames := sets.Set[string]{}
  3438  	for _, ctr := range regularContainers {
  3439  		allNames.Insert(ctr.Name)
  3440  	}
  3441  	for i, ctr := range containers {
  3442  		idxPath := fldPath.Index(i)
  3443  
  3444  		// Apply the validation common to all container types
  3445  		allErrs = append(allErrs, validateContainerCommon(&ctr, volumes, podClaimNames, idxPath, opts, podRestartPolicy)...)
  3446  
  3447  		restartAlways := false
  3448  		// Apply the validation specific to init containers
  3449  		if ctr.RestartPolicy != nil {
  3450  			allErrs = append(allErrs, validateInitContainerRestartPolicy(ctr.RestartPolicy, idxPath.Child("restartPolicy"))...)
  3451  			restartAlways = *ctr.RestartPolicy == core.ContainerRestartPolicyAlways
  3452  		}
  3453  
  3454  		// Names must be unique within regular and init containers. Collisions with ephemeral containers
  3455  		// will be detected by validateEphemeralContainers().
  3456  		if allNames.Has(ctr.Name) {
  3457  			allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), ctr.Name))
  3458  		} else if len(ctr.Name) > 0 {
  3459  			allNames.Insert(ctr.Name)
  3460  		}
  3461  
  3462  		// Check for port conflicts in init containers individually since init containers run one-by-one.
  3463  		allErrs = append(allErrs, checkHostPortConflicts([]core.Container{ctr}, fldPath)...)
  3464  
  3465  		switch {
  3466  		case restartAlways:
  3467  			if ctr.Lifecycle != nil {
  3468  				allErrs = append(allErrs, validateLifecycle(ctr.Lifecycle, gracePeriod, idxPath.Child("lifecycle"))...)
  3469  			}
  3470  			allErrs = append(allErrs, validateLivenessProbe(ctr.LivenessProbe, gracePeriod, idxPath.Child("livenessProbe"))...)
  3471  			allErrs = append(allErrs, validateReadinessProbe(ctr.ReadinessProbe, gracePeriod, idxPath.Child("readinessProbe"))...)
  3472  			allErrs = append(allErrs, validateStartupProbe(ctr.StartupProbe, gracePeriod, idxPath.Child("startupProbe"))...)
  3473  
  3474  		default:
  3475  			// These fields are disallowed for init containers.
  3476  			if ctr.Lifecycle != nil {
  3477  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("lifecycle"), "may not be set for init containers without restartPolicy=Always"))
  3478  			}
  3479  			if ctr.LivenessProbe != nil {
  3480  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("livenessProbe"), "may not be set for init containers without restartPolicy=Always"))
  3481  			}
  3482  			if ctr.ReadinessProbe != nil {
  3483  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("readinessProbe"), "may not be set for init containers without restartPolicy=Always"))
  3484  			}
  3485  			if ctr.StartupProbe != nil {
  3486  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("startupProbe"), "may not be set for init containers without restartPolicy=Always"))
  3487  			}
  3488  		}
  3489  
  3490  		if len(ctr.ResizePolicy) > 0 {
  3491  			allErrs = append(allErrs, field.Invalid(idxPath.Child("resizePolicy"), ctr.ResizePolicy, "must not be set for init containers"))
  3492  		}
  3493  	}
  3494  
  3495  	return allErrs
  3496  }
  3497  
  3498  // validateContainerCommon applies validation common to all container types. It's called by regular, init, and ephemeral
  3499  // container list validation to require a properly formatted name, image, etc.
  3500  func validateContainerCommon(ctr *core.Container, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], path *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy) field.ErrorList {
  3501  	var allErrs field.ErrorList
  3502  
  3503  	namePath := path.Child("name")
  3504  	if len(ctr.Name) == 0 {
  3505  		allErrs = append(allErrs, field.Required(namePath, ""))
  3506  	} else {
  3507  		allErrs = append(allErrs, ValidateDNS1123Label(ctr.Name, namePath)...)
  3508  	}
  3509  
  3510  	// TODO: do not validate leading and trailing whitespace to preserve backward compatibility.
  3511  	// for example: https://github.com/openshift/origin/issues/14659 image = " " is special token in pod template
  3512  	// others may have done similar
  3513  	if len(ctr.Image) == 0 {
  3514  		allErrs = append(allErrs, field.Required(path.Child("image"), ""))
  3515  	}
  3516  
  3517  	switch ctr.TerminationMessagePolicy {
  3518  	case core.TerminationMessageReadFile, core.TerminationMessageFallbackToLogsOnError:
  3519  	case "":
  3520  		allErrs = append(allErrs, field.Required(path.Child("terminationMessagePolicy"), ""))
  3521  	default:
  3522  		supported := []core.TerminationMessagePolicy{
  3523  			core.TerminationMessageReadFile,
  3524  			core.TerminationMessageFallbackToLogsOnError,
  3525  		}
  3526  		allErrs = append(allErrs, field.NotSupported(path.Child("terminationMessagePolicy"), ctr.TerminationMessagePolicy, supported))
  3527  	}
  3528  
  3529  	volMounts := GetVolumeMountMap(ctr.VolumeMounts)
  3530  	volDevices := GetVolumeDeviceMap(ctr.VolumeDevices)
  3531  	allErrs = append(allErrs, validateContainerPorts(ctr.Ports, path.Child("ports"))...)
  3532  	allErrs = append(allErrs, ValidateEnv(ctr.Env, path.Child("env"), opts)...)
  3533  	allErrs = append(allErrs, ValidateEnvFrom(ctr.EnvFrom, path.Child("envFrom"))...)
  3534  	allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, ctr, path.Child("volumeMounts"))...)
  3535  	allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, path.Child("volumeDevices"))...)
  3536  	allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, path.Child("imagePullPolicy"))...)
  3537  	allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, podClaimNames, path.Child("resources"), opts)...)
  3538  	allErrs = append(allErrs, validateResizePolicy(ctr.ResizePolicy, path.Child("resizePolicy"), podRestartPolicy)...)
  3539  	allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, path.Child("securityContext"))...)
  3540  	return allErrs
  3541  }
  3542  
  3543  func validateHostUsers(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
  3544  	allErrs := field.ErrorList{}
  3545  
  3546  	// Only make the following checks if hostUsers is false (otherwise, the container uses the
  3547  	// same userns as the host, and so there isn't anything to check).
  3548  	if spec.SecurityContext == nil || spec.SecurityContext.HostUsers == nil || *spec.SecurityContext.HostUsers {
  3549  		return allErrs
  3550  	}
  3551  
  3552  	// We decided to restrict the usage of userns with other host namespaces:
  3553  	// 	https://github.com/kubernetes/kubernetes/pull/111090#discussion_r935994282
  3554  	// The tl;dr is: you can easily run into permission issues that seem unexpected, we don't
  3555  	// know of any good use case and we can always enable them later.
  3556  
  3557  	// Note we already validated above spec.SecurityContext is not nil.
  3558  	if spec.SecurityContext.HostNetwork {
  3559  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostNetwork"), "when `pod.Spec.HostUsers` is false"))
  3560  	}
  3561  	if spec.SecurityContext.HostPID {
  3562  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("HostPID"), "when `pod.Spec.HostUsers` is false"))
  3563  	}
  3564  	if spec.SecurityContext.HostIPC {
  3565  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("HostIPC"), "when `pod.Spec.HostUsers` is false"))
  3566  	}
  3567  
  3568  	return allErrs
  3569  }
  3570  
  3571  // validateContainers is called by pod spec and template validation to validate the list of regular containers.
  3572  func validateContainers(containers []core.Container, volumes map[string]core.VolumeSource, podClaimNames sets.Set[string], gracePeriod int64, fldPath *field.Path, opts PodValidationOptions, podRestartPolicy *core.RestartPolicy) field.ErrorList {
  3573  	allErrs := field.ErrorList{}
  3574  
  3575  	if len(containers) == 0 {
  3576  		return append(allErrs, field.Required(fldPath, ""))
  3577  	}
  3578  
  3579  	allNames := sets.Set[string]{}
  3580  	for i, ctr := range containers {
  3581  		path := fldPath.Index(i)
  3582  
  3583  		// Apply validation common to all containers
  3584  		allErrs = append(allErrs, validateContainerCommon(&ctr, volumes, podClaimNames, path, opts, podRestartPolicy)...)
  3585  
  3586  		// Container names must be unique within the list of regular containers.
  3587  		// Collisions with init or ephemeral container names will be detected by the init or ephemeral
  3588  		// container validation to prevent duplicate error messages.
  3589  		if allNames.Has(ctr.Name) {
  3590  			allErrs = append(allErrs, field.Duplicate(path.Child("name"), ctr.Name))
  3591  		} else {
  3592  			allNames.Insert(ctr.Name)
  3593  		}
  3594  
  3595  		// These fields are allowed for regular containers and restartable init
  3596  		// containers.
  3597  		// Regular init container and ephemeral container validation will return
  3598  		// field.Forbidden() for these paths.
  3599  		if ctr.Lifecycle != nil {
  3600  			allErrs = append(allErrs, validateLifecycle(ctr.Lifecycle, gracePeriod, path.Child("lifecycle"))...)
  3601  		}
  3602  		allErrs = append(allErrs, validateLivenessProbe(ctr.LivenessProbe, gracePeriod, path.Child("livenessProbe"))...)
  3603  		allErrs = append(allErrs, validateReadinessProbe(ctr.ReadinessProbe, gracePeriod, path.Child("readinessProbe"))...)
  3604  		allErrs = append(allErrs, validateStartupProbe(ctr.StartupProbe, gracePeriod, path.Child("startupProbe"))...)
  3605  
  3606  		// These fields are disallowed for regular containers
  3607  		if ctr.RestartPolicy != nil {
  3608  			allErrs = append(allErrs, field.Forbidden(path.Child("restartPolicy"), "may not be set for non-init containers"))
  3609  		}
  3610  	}
  3611  
  3612  	// Port conflicts are checked across all containers
  3613  	allErrs = append(allErrs, checkHostPortConflicts(containers, fldPath)...)
  3614  
  3615  	return allErrs
  3616  }
  3617  
  3618  func validateRestartPolicy(restartPolicy *core.RestartPolicy, fldPath *field.Path) field.ErrorList {
  3619  	allErrors := field.ErrorList{}
  3620  	switch *restartPolicy {
  3621  	case core.RestartPolicyAlways, core.RestartPolicyOnFailure, core.RestartPolicyNever:
  3622  		break
  3623  	case "":
  3624  		allErrors = append(allErrors, field.Required(fldPath, ""))
  3625  	default:
  3626  		validValues := []core.RestartPolicy{core.RestartPolicyAlways, core.RestartPolicyOnFailure, core.RestartPolicyNever}
  3627  		allErrors = append(allErrors, field.NotSupported(fldPath, *restartPolicy, validValues))
  3628  	}
  3629  
  3630  	return allErrors
  3631  }
  3632  
  3633  func ValidatePreemptionPolicy(preemptionPolicy *core.PreemptionPolicy, fldPath *field.Path) field.ErrorList {
  3634  	allErrors := field.ErrorList{}
  3635  	switch *preemptionPolicy {
  3636  	case core.PreemptLowerPriority, core.PreemptNever:
  3637  	case "":
  3638  		allErrors = append(allErrors, field.Required(fldPath, ""))
  3639  	default:
  3640  		validValues := []core.PreemptionPolicy{core.PreemptLowerPriority, core.PreemptNever}
  3641  		allErrors = append(allErrors, field.NotSupported(fldPath, preemptionPolicy, validValues))
  3642  	}
  3643  	return allErrors
  3644  }
  3645  
  3646  func validateDNSPolicy(dnsPolicy *core.DNSPolicy, fldPath *field.Path) field.ErrorList {
  3647  	allErrors := field.ErrorList{}
  3648  	switch *dnsPolicy {
  3649  	case core.DNSClusterFirstWithHostNet, core.DNSClusterFirst, core.DNSDefault, core.DNSNone:
  3650  	case "":
  3651  		allErrors = append(allErrors, field.Required(fldPath, ""))
  3652  	default:
  3653  		validValues := []core.DNSPolicy{core.DNSClusterFirstWithHostNet, core.DNSClusterFirst, core.DNSDefault, core.DNSNone}
  3654  		allErrors = append(allErrors, field.NotSupported(fldPath, dnsPolicy, validValues))
  3655  	}
  3656  	return allErrors
  3657  }
  3658  
  3659  var validFSGroupChangePolicies = sets.New(core.FSGroupChangeOnRootMismatch, core.FSGroupChangeAlways)
  3660  
  3661  func validateFSGroupChangePolicy(fsGroupPolicy *core.PodFSGroupChangePolicy, fldPath *field.Path) field.ErrorList {
  3662  	allErrors := field.ErrorList{}
  3663  	if !validFSGroupChangePolicies.Has(*fsGroupPolicy) {
  3664  		allErrors = append(allErrors, field.NotSupported(fldPath, fsGroupPolicy, sets.List(validFSGroupChangePolicies)))
  3665  	}
  3666  	return allErrors
  3667  }
  3668  
  3669  const (
  3670  	// Limits on various DNS parameters. These are derived from
  3671  	// restrictions in Linux libc name resolution handling.
  3672  	// Max number of DNS name servers.
  3673  	MaxDNSNameservers = 3
  3674  	// Max number of domains in the search path list.
  3675  	MaxDNSSearchPaths = 32
  3676  	// Max number of characters in the search path.
  3677  	MaxDNSSearchListChars = 2048
  3678  )
  3679  
  3680  func validateReadinessGates(readinessGates []core.PodReadinessGate, fldPath *field.Path) field.ErrorList {
  3681  	allErrs := field.ErrorList{}
  3682  	for i, value := range readinessGates {
  3683  		allErrs = append(allErrs, ValidateQualifiedName(string(value.ConditionType), fldPath.Index(i).Child("conditionType"))...)
  3684  	}
  3685  	return allErrs
  3686  }
  3687  
  3688  func validateSchedulingGates(schedulingGates []core.PodSchedulingGate, fldPath *field.Path) field.ErrorList {
  3689  	allErrs := field.ErrorList{}
  3690  	// There should be no duplicates in the list of scheduling gates.
  3691  	seen := sets.Set[string]{}
  3692  	for i, schedulingGate := range schedulingGates {
  3693  		allErrs = append(allErrs, ValidateQualifiedName(schedulingGate.Name, fldPath.Index(i))...)
  3694  		if seen.Has(schedulingGate.Name) {
  3695  			allErrs = append(allErrs, field.Duplicate(fldPath.Index(i), schedulingGate.Name))
  3696  		}
  3697  		seen.Insert(schedulingGate.Name)
  3698  	}
  3699  	return allErrs
  3700  }
  3701  
  3702  func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolicy, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  3703  	allErrs := field.ErrorList{}
  3704  
  3705  	// Validate DNSNone case. Must provide at least one DNS name server.
  3706  	if dnsPolicy != nil && *dnsPolicy == core.DNSNone {
  3707  		if dnsConfig == nil {
  3708  			return append(allErrs, field.Required(fldPath, fmt.Sprintf("must provide `dnsConfig` when `dnsPolicy` is %s", core.DNSNone)))
  3709  		}
  3710  		if len(dnsConfig.Nameservers) == 0 {
  3711  			return append(allErrs, field.Required(fldPath.Child("nameservers"), fmt.Sprintf("must provide at least one DNS nameserver when `dnsPolicy` is %s", core.DNSNone)))
  3712  		}
  3713  	}
  3714  
  3715  	if dnsConfig != nil {
  3716  		// Validate nameservers.
  3717  		if len(dnsConfig.Nameservers) > MaxDNSNameservers {
  3718  			allErrs = append(allErrs, field.Invalid(fldPath.Child("nameservers"), dnsConfig.Nameservers, fmt.Sprintf("must not have more than %v nameservers", MaxDNSNameservers)))
  3719  		}
  3720  		for i, ns := range dnsConfig.Nameservers {
  3721  			if ip := netutils.ParseIPSloppy(ns); ip == nil {
  3722  				allErrs = append(allErrs, field.Invalid(fldPath.Child("nameservers").Index(i), ns, "must be valid IP address"))
  3723  			}
  3724  		}
  3725  		// Validate searches.
  3726  		if len(dnsConfig.Searches) > MaxDNSSearchPaths {
  3727  			allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, fmt.Sprintf("must not have more than %v search paths", MaxDNSSearchPaths)))
  3728  		}
  3729  		// Include the space between search paths.
  3730  		if len(strings.Join(dnsConfig.Searches, " ")) > MaxDNSSearchListChars {
  3731  			allErrs = append(allErrs, field.Invalid(fldPath.Child("searches"), dnsConfig.Searches, fmt.Sprintf("must not have more than %v characters (including spaces) in the search list", MaxDNSSearchListChars)))
  3732  		}
  3733  		for i, search := range dnsConfig.Searches {
  3734  			// it is fine to have a trailing dot
  3735  			search = strings.TrimSuffix(search, ".")
  3736  			allErrs = append(allErrs, ValidateDNS1123Subdomain(search, fldPath.Child("searches").Index(i))...)
  3737  		}
  3738  		// Validate options.
  3739  		for i, option := range dnsConfig.Options {
  3740  			if len(option.Name) == 0 {
  3741  				allErrs = append(allErrs, field.Required(fldPath.Child("options").Index(i), "must not be empty"))
  3742  			}
  3743  		}
  3744  	}
  3745  	return allErrs
  3746  }
  3747  
  3748  // validatePodHostNetworkDeps checks fields which depend on whether HostNetwork is
  3749  // true or not.  It should be called on all PodSpecs, but opts can change what
  3750  // is enforce.  E.g. opts.ResourceIsPod should only be set when called in the
  3751  // context of a Pod, and not on PodSpecs which are embedded in other resources
  3752  // (e.g. Deployments).
  3753  func validatePodHostNetworkDeps(spec *core.PodSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  3754  	// For <reasons> we keep `.HostNetwork` in .SecurityContext on the internal
  3755  	// version of Pod.
  3756  	hostNetwork := false
  3757  	if spec.SecurityContext != nil {
  3758  		hostNetwork = spec.SecurityContext.HostNetwork
  3759  	}
  3760  
  3761  	allErrors := field.ErrorList{}
  3762  
  3763  	if hostNetwork {
  3764  		fldPath := fldPath.Child("containers")
  3765  		for i, container := range spec.Containers {
  3766  			portsPath := fldPath.Index(i).Child("ports")
  3767  			for i, port := range container.Ports {
  3768  				idxPath := portsPath.Index(i)
  3769  				// At this point, we know that HostNetwork is true. If this
  3770  				// PodSpec is in a Pod (opts.ResourceIsPod), then HostPort must
  3771  				// be the same value as ContainerPort. If this PodSpec is in
  3772  				// some other resource (e.g. Deployment) we allow 0 (i.e.
  3773  				// unspecified) because it will be defaulted when the Pod is
  3774  				// ultimately created, but we do not allow any other values.
  3775  				if hp, cp := port.HostPort, port.ContainerPort; (opts.ResourceIsPod || hp != 0) && hp != cp {
  3776  					allErrors = append(allErrors, field.Invalid(idxPath.Child("hostPort"), port.HostPort, "must match `containerPort` when `hostNetwork` is true"))
  3777  				}
  3778  			}
  3779  		}
  3780  	}
  3781  	return allErrors
  3782  }
  3783  
  3784  // validateImagePullSecrets checks to make sure the pull secrets are well
  3785  // formed.  Right now, we only expect name to be set (it's the only field).  If
  3786  // this ever changes and someone decides to set those fields, we'd like to
  3787  // know.
  3788  func validateImagePullSecrets(imagePullSecrets []core.LocalObjectReference, fldPath *field.Path) field.ErrorList {
  3789  	allErrors := field.ErrorList{}
  3790  	for i, currPullSecret := range imagePullSecrets {
  3791  		idxPath := fldPath.Index(i)
  3792  		strippedRef := core.LocalObjectReference{Name: currPullSecret.Name}
  3793  		if !reflect.DeepEqual(strippedRef, currPullSecret) {
  3794  			allErrors = append(allErrors, field.Invalid(idxPath, currPullSecret, "only name may be set"))
  3795  		}
  3796  	}
  3797  	return allErrors
  3798  }
  3799  
  3800  // validateAffinity checks if given affinities are valid
  3801  func validateAffinity(affinity *core.Affinity, opts PodValidationOptions, fldPath *field.Path) field.ErrorList {
  3802  	allErrs := field.ErrorList{}
  3803  
  3804  	if affinity != nil {
  3805  		if affinity.NodeAffinity != nil {
  3806  			allErrs = append(allErrs, validateNodeAffinity(affinity.NodeAffinity, fldPath.Child("nodeAffinity"))...)
  3807  		}
  3808  		if affinity.PodAffinity != nil {
  3809  			allErrs = append(allErrs, validatePodAffinity(affinity.PodAffinity, opts.AllowInvalidLabelValueInSelector, fldPath.Child("podAffinity"))...)
  3810  		}
  3811  		if affinity.PodAntiAffinity != nil {
  3812  			allErrs = append(allErrs, validatePodAntiAffinity(affinity.PodAntiAffinity, opts.AllowInvalidLabelValueInSelector, fldPath.Child("podAntiAffinity"))...)
  3813  		}
  3814  	}
  3815  
  3816  	return allErrs
  3817  }
  3818  
  3819  func validateTaintEffect(effect *core.TaintEffect, allowEmpty bool, fldPath *field.Path) field.ErrorList {
  3820  	if !allowEmpty && len(*effect) == 0 {
  3821  		return field.ErrorList{field.Required(fldPath, "")}
  3822  	}
  3823  
  3824  	allErrors := field.ErrorList{}
  3825  	switch *effect {
  3826  	// TODO: Replace next line with subsequent commented-out line when implement TaintEffectNoScheduleNoAdmit.
  3827  	case core.TaintEffectNoSchedule, core.TaintEffectPreferNoSchedule, core.TaintEffectNoExecute:
  3828  		// case core.TaintEffectNoSchedule, core.TaintEffectPreferNoSchedule, core.TaintEffectNoScheduleNoAdmit, core.TaintEffectNoExecute:
  3829  	default:
  3830  		validValues := []core.TaintEffect{
  3831  			core.TaintEffectNoSchedule,
  3832  			core.TaintEffectPreferNoSchedule,
  3833  			core.TaintEffectNoExecute,
  3834  			// TODO: Uncomment this block when implement TaintEffectNoScheduleNoAdmit.
  3835  			// core.TaintEffectNoScheduleNoAdmit,
  3836  		}
  3837  		allErrors = append(allErrors, field.NotSupported(fldPath, *effect, validValues))
  3838  	}
  3839  	return allErrors
  3840  }
  3841  
  3842  // validateOnlyAddedTolerations validates updated pod tolerations.
  3843  func validateOnlyAddedTolerations(newTolerations []core.Toleration, oldTolerations []core.Toleration, fldPath *field.Path) field.ErrorList {
  3844  	allErrs := field.ErrorList{}
  3845  	for _, old := range oldTolerations {
  3846  		found := false
  3847  		oldTolerationClone := old.DeepCopy()
  3848  		for _, newToleration := range newTolerations {
  3849  			// assign to our clone before doing a deep equal so we can allow tolerationseconds to change.
  3850  			oldTolerationClone.TolerationSeconds = newToleration.TolerationSeconds // +k8s:verify-mutation:reason=clone
  3851  			if reflect.DeepEqual(*oldTolerationClone, newToleration) {
  3852  				found = true
  3853  				break
  3854  			}
  3855  		}
  3856  		if !found {
  3857  			allErrs = append(allErrs, field.Forbidden(fldPath, "existing toleration can not be modified except its tolerationSeconds"))
  3858  			return allErrs
  3859  		}
  3860  	}
  3861  
  3862  	allErrs = append(allErrs, ValidateTolerations(newTolerations, fldPath)...)
  3863  	return allErrs
  3864  }
  3865  
  3866  func validateOnlyDeletedSchedulingGates(newGates, oldGates []core.PodSchedulingGate, fldPath *field.Path) field.ErrorList {
  3867  	allErrs := field.ErrorList{}
  3868  	if len(newGates) == 0 {
  3869  		return allErrs
  3870  	}
  3871  
  3872  	additionalGates := make(map[string]int)
  3873  	for i, newGate := range newGates {
  3874  		additionalGates[newGate.Name] = i
  3875  	}
  3876  
  3877  	for _, oldGate := range oldGates {
  3878  		delete(additionalGates, oldGate.Name)
  3879  	}
  3880  
  3881  	for gate, i := range additionalGates {
  3882  		allErrs = append(allErrs, field.Forbidden(fldPath.Index(i).Child("name"), fmt.Sprintf("only deletion is allowed, but found new scheduling gate '%s'", gate)))
  3883  	}
  3884  
  3885  	return allErrs
  3886  }
  3887  
  3888  func ValidateHostAliases(hostAliases []core.HostAlias, fldPath *field.Path) field.ErrorList {
  3889  	allErrs := field.ErrorList{}
  3890  	for _, hostAlias := range hostAliases {
  3891  		if ip := netutils.ParseIPSloppy(hostAlias.IP); ip == nil {
  3892  			allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), hostAlias.IP, "must be valid IP address"))
  3893  		}
  3894  		for _, hostname := range hostAlias.Hostnames {
  3895  			allErrs = append(allErrs, ValidateDNS1123Subdomain(hostname, fldPath.Child("hostnames"))...)
  3896  		}
  3897  	}
  3898  	return allErrs
  3899  }
  3900  
  3901  // ValidateTolerations tests if given tolerations have valid data.
  3902  func ValidateTolerations(tolerations []core.Toleration, fldPath *field.Path) field.ErrorList {
  3903  	allErrors := field.ErrorList{}
  3904  	for i, toleration := range tolerations {
  3905  		idxPath := fldPath.Index(i)
  3906  		// validate the toleration key
  3907  		if len(toleration.Key) > 0 {
  3908  			allErrors = append(allErrors, unversionedvalidation.ValidateLabelName(toleration.Key, idxPath.Child("key"))...)
  3909  		}
  3910  
  3911  		// empty toleration key with Exists operator and empty value means match all taints
  3912  		if len(toleration.Key) == 0 && toleration.Operator != core.TolerationOpExists {
  3913  			allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration.Operator,
  3914  				"operator must be Exists when `key` is empty, which means \"match all values and all keys\""))
  3915  		}
  3916  
  3917  		if toleration.TolerationSeconds != nil && toleration.Effect != core.TaintEffectNoExecute {
  3918  			allErrors = append(allErrors, field.Invalid(idxPath.Child("effect"), toleration.Effect,
  3919  				"effect must be 'NoExecute' when `tolerationSeconds` is set"))
  3920  		}
  3921  
  3922  		// validate toleration operator and value
  3923  		switch toleration.Operator {
  3924  		// empty operator means Equal
  3925  		case core.TolerationOpEqual, "":
  3926  			if errs := validation.IsValidLabelValue(toleration.Value); len(errs) != 0 {
  3927  				allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration.Value, strings.Join(errs, ";")))
  3928  			}
  3929  		case core.TolerationOpExists:
  3930  			if len(toleration.Value) > 0 {
  3931  				allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration, "value must be empty when `operator` is 'Exists'"))
  3932  			}
  3933  		default:
  3934  			validValues := []core.TolerationOperator{core.TolerationOpEqual, core.TolerationOpExists}
  3935  			allErrors = append(allErrors, field.NotSupported(idxPath.Child("operator"), toleration.Operator, validValues))
  3936  		}
  3937  
  3938  		// validate toleration effect, empty toleration effect means match all taint effects
  3939  		if len(toleration.Effect) > 0 {
  3940  			allErrors = append(allErrors, validateTaintEffect(&toleration.Effect, true, idxPath.Child("effect"))...)
  3941  		}
  3942  	}
  3943  	return allErrors
  3944  }
  3945  
  3946  // validateContainersOnlyForPod does additional validation for containers on a pod versus a pod template
  3947  // it only does additive validation of fields not covered in validateContainers and is not called for
  3948  // ephemeral containers which require a conversion to core.Container.
  3949  func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Path) field.ErrorList {
  3950  	allErrs := field.ErrorList{}
  3951  	for i, ctr := range containers {
  3952  		allErrs = append(allErrs, validateContainerOnlyForPod(&ctr, fldPath.Index(i))...)
  3953  	}
  3954  	return allErrs
  3955  }
  3956  
  3957  // validateContainerOnlyForPod does pod-only (i.e. not pod template) validation for a single container.
  3958  // This is called by validateContainersOnlyForPod and validateEphemeralContainers directly.
  3959  func validateContainerOnlyForPod(ctr *core.Container, path *field.Path) field.ErrorList {
  3960  	allErrs := field.ErrorList{}
  3961  	if len(ctr.Image) != len(strings.TrimSpace(ctr.Image)) {
  3962  		allErrs = append(allErrs, field.Invalid(path.Child("image"), ctr.Image, "must not have leading or trailing whitespace"))
  3963  	}
  3964  	return allErrs
  3965  }
  3966  
  3967  // PodValidationOptions contains the different settings for pod validation
  3968  type PodValidationOptions struct {
  3969  	// Allow invalid pod-deletion-cost annotation value for backward compatibility.
  3970  	AllowInvalidPodDeletionCost bool
  3971  	// Allow invalid label-value in LabelSelector
  3972  	AllowInvalidLabelValueInSelector bool
  3973  	// Allow pod spec to use non-integer multiple of huge page unit size
  3974  	AllowIndivisibleHugePagesValues bool
  3975  	// Allow pod spec to use status.hostIPs in downward API if feature is enabled
  3976  	AllowHostIPsField bool
  3977  	// Allow invalid topologySpreadConstraint labelSelector for backward compatibility
  3978  	AllowInvalidTopologySpreadConstraintLabelSelector bool
  3979  	// Allow node selector additions for gated pods.
  3980  	AllowMutableNodeSelectorAndNodeAffinity bool
  3981  	// Allow namespaced sysctls in hostNet and hostIPC pods
  3982  	AllowNamespacedSysctlsForHostNetAndHostIPC bool
  3983  	// The top-level resource being validated is a Pod, not just a PodSpec
  3984  	// embedded in some other resource.
  3985  	ResourceIsPod bool
  3986  }
  3987  
  3988  // validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,
  3989  // and is called by ValidatePodCreate and ValidatePodUpdate.
  3990  func validatePodMetadataAndSpec(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
  3991  	metaPath := field.NewPath("metadata")
  3992  	specPath := field.NewPath("spec")
  3993  
  3994  	allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, metaPath)
  3995  	allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, metaPath.Child("annotations"), opts)...)
  3996  	allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, &pod.ObjectMeta, specPath, opts)...)
  3997  
  3998  	// we do additional validation only pertinent for pods and not pod templates
  3999  	// this was done to preserve backwards compatibility
  4000  
  4001  	if pod.Spec.ServiceAccountName == "" {
  4002  		for vi, volume := range pod.Spec.Volumes {
  4003  			path := specPath.Child("volumes").Index(vi).Child("projected")
  4004  			if volume.Projected != nil {
  4005  				for si, source := range volume.Projected.Sources {
  4006  					saPath := path.Child("sources").Index(si).Child("serviceAccountToken")
  4007  					if source.ServiceAccountToken != nil {
  4008  						allErrs = append(allErrs, field.Forbidden(saPath, "must not be specified when serviceAccountName is not set"))
  4009  					}
  4010  				}
  4011  			}
  4012  		}
  4013  	}
  4014  
  4015  	allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.Containers, specPath.Child("containers"))...)
  4016  	allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.InitContainers, specPath.Child("initContainers"))...)
  4017  	// validateContainersOnlyForPod() is checked for ephemeral containers by validateEphemeralContainers()
  4018  
  4019  	return allErrs
  4020  }
  4021  
  4022  // validatePodIPs validates IPs in pod status
  4023  func validatePodIPs(pod *core.Pod) field.ErrorList {
  4024  	allErrs := field.ErrorList{}
  4025  
  4026  	podIPsField := field.NewPath("status", "podIPs")
  4027  
  4028  	// all PodIPs must be valid IPs
  4029  	for i, podIP := range pod.Status.PodIPs {
  4030  		for _, msg := range validation.IsValidIP(podIP.IP) {
  4031  			allErrs = append(allErrs, field.Invalid(podIPsField.Index(i), podIP.IP, msg))
  4032  		}
  4033  	}
  4034  
  4035  	// if we have more than one Pod.PodIP then
  4036  	// - validate for dual stack
  4037  	// - validate for duplication
  4038  	if len(pod.Status.PodIPs) > 1 {
  4039  		podIPs := make([]string, 0, len(pod.Status.PodIPs))
  4040  		for _, podIP := range pod.Status.PodIPs {
  4041  			podIPs = append(podIPs, podIP.IP)
  4042  		}
  4043  
  4044  		dualStack, err := netutils.IsDualStackIPStrings(podIPs)
  4045  		if err != nil {
  4046  			allErrs = append(allErrs, field.InternalError(podIPsField, fmt.Errorf("failed to check for dual stack with error:%v", err)))
  4047  		}
  4048  
  4049  		// We only support one from each IP family (i.e. max two IPs in this list).
  4050  		if !dualStack || len(podIPs) > 2 {
  4051  			allErrs = append(allErrs, field.Invalid(podIPsField, pod.Status.PodIPs, "may specify no more than one IP for each IP family"))
  4052  		}
  4053  
  4054  		// There should be no duplicates in list of Pod.PodIPs
  4055  		seen := sets.Set[string]{} // := make(map[string]int)
  4056  		for i, podIP := range pod.Status.PodIPs {
  4057  			if seen.Has(podIP.IP) {
  4058  				allErrs = append(allErrs, field.Duplicate(podIPsField.Index(i), podIP))
  4059  			}
  4060  			seen.Insert(podIP.IP)
  4061  		}
  4062  	}
  4063  
  4064  	return allErrs
  4065  }
  4066  
  4067  // validateHostIPs validates IPs in pod status
  4068  func validateHostIPs(pod *core.Pod) field.ErrorList {
  4069  	allErrs := field.ErrorList{}
  4070  
  4071  	if len(pod.Status.HostIPs) == 0 {
  4072  		return allErrs
  4073  	}
  4074  
  4075  	hostIPsField := field.NewPath("status", "hostIPs")
  4076  
  4077  	// hostIP must be equal to hostIPs[0].IP
  4078  	if pod.Status.HostIP != pod.Status.HostIPs[0].IP {
  4079  		allErrs = append(allErrs, field.Invalid(hostIPsField.Index(0).Child("ip"), pod.Status.HostIPs[0].IP, "must be equal to `hostIP`"))
  4080  	}
  4081  
  4082  	// all HostPs must be valid IPs
  4083  	for i, hostIP := range pod.Status.HostIPs {
  4084  		for _, msg := range validation.IsValidIP(hostIP.IP) {
  4085  			allErrs = append(allErrs, field.Invalid(hostIPsField.Index(i), hostIP.IP, msg))
  4086  		}
  4087  	}
  4088  
  4089  	// if we have more than one Pod.HostIP then
  4090  	// - validate for dual stack
  4091  	// - validate for duplication
  4092  	if len(pod.Status.HostIPs) > 1 {
  4093  		seen := sets.Set[string]{}
  4094  		hostIPs := make([]string, 0, len(pod.Status.HostIPs))
  4095  
  4096  		// There should be no duplicates in list of Pod.HostIPs
  4097  		for i, hostIP := range pod.Status.HostIPs {
  4098  			hostIPs = append(hostIPs, hostIP.IP)
  4099  			if seen.Has(hostIP.IP) {
  4100  				allErrs = append(allErrs, field.Duplicate(hostIPsField.Index(i), hostIP))
  4101  			}
  4102  			seen.Insert(hostIP.IP)
  4103  		}
  4104  
  4105  		dualStack, err := netutils.IsDualStackIPStrings(hostIPs)
  4106  		if err != nil {
  4107  			allErrs = append(allErrs, field.InternalError(hostIPsField, fmt.Errorf("failed to check for dual stack with error:%v", err)))
  4108  		}
  4109  
  4110  		// We only support one from each IP family (i.e. max two IPs in this list).
  4111  		if !dualStack || len(hostIPs) > 2 {
  4112  			allErrs = append(allErrs, field.Invalid(hostIPsField, pod.Status.HostIPs, "may specify no more than one IP for each IP family"))
  4113  		}
  4114  	}
  4115  
  4116  	return allErrs
  4117  }
  4118  
  4119  // ValidatePodSpec tests that the specified PodSpec has valid data.
  4120  // This includes checking formatting and uniqueness.  It also canonicalizes the
  4121  // structure by setting default values and implementing any backwards-compatibility
  4122  // tricks.
  4123  // The pod metadata is needed to validate generic ephemeral volumes. It is optional
  4124  // and should be left empty unless the spec is from a real pod object.
  4125  func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  4126  	allErrs := field.ErrorList{}
  4127  
  4128  	var gracePeriod int64
  4129  	if spec.TerminationGracePeriodSeconds != nil {
  4130  		// this could happen in tests
  4131  		gracePeriod = *spec.TerminationGracePeriodSeconds
  4132  	}
  4133  
  4134  	vols, vErrs := ValidateVolumes(spec.Volumes, podMeta, fldPath.Child("volumes"), opts)
  4135  	allErrs = append(allErrs, vErrs...)
  4136  	podClaimNames := gatherPodResourceClaimNames(spec.ResourceClaims)
  4137  	allErrs = append(allErrs, validatePodResourceClaims(podMeta, spec.ResourceClaims, fldPath.Child("resourceClaims"))...)
  4138  	allErrs = append(allErrs, validateContainers(spec.Containers, vols, podClaimNames, gracePeriod, fldPath.Child("containers"), opts, &spec.RestartPolicy)...)
  4139  	allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, podClaimNames, gracePeriod, fldPath.Child("initContainers"), opts, &spec.RestartPolicy)...)
  4140  	allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, podClaimNames, fldPath.Child("ephemeralContainers"), opts, &spec.RestartPolicy)...)
  4141  	allErrs = append(allErrs, validatePodHostNetworkDeps(spec, fldPath, opts)...)
  4142  	allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
  4143  	allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
  4144  	allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
  4145  	allErrs = append(allErrs, validatePodSpecSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"), opts)...)
  4146  	allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
  4147  	allErrs = append(allErrs, validateAffinity(spec.Affinity, opts, fldPath.Child("affinity"))...)
  4148  	allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"), opts)...)
  4149  	allErrs = append(allErrs, validateReadinessGates(spec.ReadinessGates, fldPath.Child("readinessGates"))...)
  4150  	allErrs = append(allErrs, validateSchedulingGates(spec.SchedulingGates, fldPath.Child("schedulingGates"))...)
  4151  	allErrs = append(allErrs, validateTopologySpreadConstraints(spec.TopologySpreadConstraints, fldPath.Child("topologySpreadConstraints"), opts)...)
  4152  	allErrs = append(allErrs, validateWindowsHostProcessPod(spec, fldPath)...)
  4153  	allErrs = append(allErrs, validateHostUsers(spec, fldPath)...)
  4154  	if len(spec.ServiceAccountName) > 0 {
  4155  		for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
  4156  			allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
  4157  		}
  4158  	}
  4159  
  4160  	if len(spec.NodeName) > 0 {
  4161  		for _, msg := range ValidateNodeName(spec.NodeName, false) {
  4162  			allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), spec.NodeName, msg))
  4163  		}
  4164  	}
  4165  
  4166  	if spec.ActiveDeadlineSeconds != nil {
  4167  		value := *spec.ActiveDeadlineSeconds
  4168  		if value < 1 || value > math.MaxInt32 {
  4169  			allErrs = append(allErrs, field.Invalid(fldPath.Child("activeDeadlineSeconds"), value, validation.InclusiveRangeError(1, math.MaxInt32)))
  4170  		}
  4171  	}
  4172  
  4173  	if len(spec.Hostname) > 0 {
  4174  		allErrs = append(allErrs, ValidateDNS1123Label(spec.Hostname, fldPath.Child("hostname"))...)
  4175  	}
  4176  
  4177  	if len(spec.Subdomain) > 0 {
  4178  		allErrs = append(allErrs, ValidateDNS1123Label(spec.Subdomain, fldPath.Child("subdomain"))...)
  4179  	}
  4180  
  4181  	if len(spec.Tolerations) > 0 {
  4182  		allErrs = append(allErrs, ValidateTolerations(spec.Tolerations, fldPath.Child("tolerations"))...)
  4183  	}
  4184  
  4185  	if len(spec.HostAliases) > 0 {
  4186  		allErrs = append(allErrs, ValidateHostAliases(spec.HostAliases, fldPath.Child("hostAliases"))...)
  4187  	}
  4188  
  4189  	if len(spec.PriorityClassName) > 0 {
  4190  		for _, msg := range ValidatePriorityClassName(spec.PriorityClassName, false) {
  4191  			allErrs = append(allErrs, field.Invalid(fldPath.Child("priorityClassName"), spec.PriorityClassName, msg))
  4192  		}
  4193  	}
  4194  
  4195  	if spec.RuntimeClassName != nil {
  4196  		allErrs = append(allErrs, ValidateRuntimeClassName(*spec.RuntimeClassName, fldPath.Child("runtimeClassName"))...)
  4197  	}
  4198  
  4199  	if spec.PreemptionPolicy != nil {
  4200  		allErrs = append(allErrs, ValidatePreemptionPolicy(spec.PreemptionPolicy, fldPath.Child("preemptionPolicy"))...)
  4201  	}
  4202  
  4203  	if spec.Overhead != nil {
  4204  		allErrs = append(allErrs, validateOverhead(spec.Overhead, fldPath.Child("overhead"), opts)...)
  4205  	}
  4206  
  4207  	if spec.OS != nil {
  4208  		osErrs := validateOS(spec, fldPath.Child("os"), opts)
  4209  		switch {
  4210  		case len(osErrs) > 0:
  4211  			allErrs = append(allErrs, osErrs...)
  4212  		case spec.OS.Name == core.Linux:
  4213  			allErrs = append(allErrs, validateLinux(spec, fldPath)...)
  4214  		case spec.OS.Name == core.Windows:
  4215  			allErrs = append(allErrs, validateWindows(spec, fldPath)...)
  4216  		}
  4217  	}
  4218  	return allErrs
  4219  }
  4220  
  4221  func validateLinux(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
  4222  	allErrs := field.ErrorList{}
  4223  	securityContext := spec.SecurityContext
  4224  	if securityContext != nil && securityContext.WindowsOptions != nil {
  4225  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("windowsOptions"), "windows options cannot be set for a linux pod"))
  4226  	}
  4227  	podshelper.VisitContainersWithPath(spec, fldPath, func(c *core.Container, cFldPath *field.Path) bool {
  4228  		sc := c.SecurityContext
  4229  		if sc != nil && sc.WindowsOptions != nil {
  4230  			fldPath := cFldPath.Child("securityContext")
  4231  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("windowsOptions"), "windows options cannot be set for a linux pod"))
  4232  		}
  4233  		return true
  4234  	})
  4235  	return allErrs
  4236  }
  4237  
  4238  func validateWindows(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
  4239  	allErrs := field.ErrorList{}
  4240  	securityContext := spec.SecurityContext
  4241  	// validate Pod SecurityContext
  4242  	if securityContext != nil {
  4243  		if securityContext.SELinuxOptions != nil {
  4244  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("seLinuxOptions"), "cannot be set for a windows pod"))
  4245  		}
  4246  		if securityContext.HostUsers != nil {
  4247  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostUsers"), "cannot be set for a windows pod"))
  4248  		}
  4249  		if securityContext.HostPID {
  4250  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostPID"), "cannot be set for a windows pod"))
  4251  		}
  4252  		if securityContext.HostIPC {
  4253  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostIPC"), "cannot be set for a windows pod"))
  4254  		}
  4255  		if securityContext.SeccompProfile != nil {
  4256  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("seccompProfile"), "cannot be set for a windows pod"))
  4257  		}
  4258  		if securityContext.FSGroup != nil {
  4259  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("fsGroup"), "cannot be set for a windows pod"))
  4260  		}
  4261  		if securityContext.FSGroupChangePolicy != nil {
  4262  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("fsGroupChangePolicy"), "cannot be set for a windows pod"))
  4263  		}
  4264  		if len(securityContext.Sysctls) > 0 {
  4265  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("sysctls"), "cannot be set for a windows pod"))
  4266  		}
  4267  		if securityContext.ShareProcessNamespace != nil {
  4268  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("shareProcessNamespace"), "cannot be set for a windows pod"))
  4269  		}
  4270  		if securityContext.RunAsUser != nil {
  4271  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("runAsUser"), "cannot be set for a windows pod"))
  4272  		}
  4273  		if securityContext.RunAsGroup != nil {
  4274  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("runAsGroup"), "cannot be set for a windows pod"))
  4275  		}
  4276  		if securityContext.SupplementalGroups != nil {
  4277  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("securityContext").Child("supplementalGroups"), "cannot be set for a windows pod"))
  4278  		}
  4279  	}
  4280  	podshelper.VisitContainersWithPath(spec, fldPath, func(c *core.Container, cFldPath *field.Path) bool {
  4281  		// validate container security context
  4282  		sc := c.SecurityContext
  4283  		// OS based podSecurityContext validation
  4284  		// There is some naming overlap between Windows and Linux Security Contexts but all the Windows Specific options
  4285  		// are set via securityContext.WindowsOptions which we validate below
  4286  		// TODO: Think if we need to relax this restriction or some of the restrictions
  4287  		if sc != nil {
  4288  			fldPath := cFldPath.Child("securityContext")
  4289  			if sc.SELinuxOptions != nil {
  4290  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("seLinuxOptions"), "cannot be set for a windows pod"))
  4291  			}
  4292  			if sc.SeccompProfile != nil {
  4293  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("seccompProfile"), "cannot be set for a windows pod"))
  4294  			}
  4295  			if sc.Capabilities != nil {
  4296  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("capabilities"), "cannot be set for a windows pod"))
  4297  			}
  4298  			if sc.ReadOnlyRootFilesystem != nil {
  4299  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("readOnlyRootFilesystem"), "cannot be set for a windows pod"))
  4300  			}
  4301  			if sc.Privileged != nil {
  4302  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("privileged"), "cannot be set for a windows pod"))
  4303  			}
  4304  			if sc.AllowPrivilegeEscalation != nil {
  4305  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("allowPrivilegeEscalation"), "cannot be set for a windows pod"))
  4306  			}
  4307  			if sc.ProcMount != nil {
  4308  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("procMount"), "cannot be set for a windows pod"))
  4309  			}
  4310  			if sc.RunAsUser != nil {
  4311  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("runAsUser"), "cannot be set for a windows pod"))
  4312  			}
  4313  			if sc.RunAsGroup != nil {
  4314  				allErrs = append(allErrs, field.Forbidden(fldPath.Child("runAsGroup"), "cannot be set for a windows pod"))
  4315  			}
  4316  		}
  4317  		return true
  4318  	})
  4319  	return allErrs
  4320  }
  4321  
  4322  // ValidateNodeSelectorRequirement tests that the specified NodeSelectorRequirement fields has valid data
  4323  func ValidateNodeSelectorRequirement(rq core.NodeSelectorRequirement, fldPath *field.Path) field.ErrorList {
  4324  	allErrs := field.ErrorList{}
  4325  	switch rq.Operator {
  4326  	case core.NodeSelectorOpIn, core.NodeSelectorOpNotIn:
  4327  		if len(rq.Values) == 0 {
  4328  			allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified when `operator` is 'In' or 'NotIn'"))
  4329  		}
  4330  	case core.NodeSelectorOpExists, core.NodeSelectorOpDoesNotExist:
  4331  		if len(rq.Values) > 0 {
  4332  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
  4333  		}
  4334  
  4335  	case core.NodeSelectorOpGt, core.NodeSelectorOpLt:
  4336  		if len(rq.Values) != 1 {
  4337  			allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified single value when `operator` is 'Lt' or 'Gt'"))
  4338  		}
  4339  	default:
  4340  		allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), rq.Operator, "not a valid selector operator"))
  4341  	}
  4342  
  4343  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(rq.Key, fldPath.Child("key"))...)
  4344  
  4345  	return allErrs
  4346  }
  4347  
  4348  var nodeFieldSelectorValidators = map[string]func(string, bool) []string{
  4349  	metav1.ObjectNameField: ValidateNodeName,
  4350  }
  4351  
  4352  // ValidateNodeFieldSelectorRequirement tests that the specified NodeSelectorRequirement fields has valid data
  4353  func ValidateNodeFieldSelectorRequirement(req core.NodeSelectorRequirement, fldPath *field.Path) field.ErrorList {
  4354  	allErrs := field.ErrorList{}
  4355  
  4356  	switch req.Operator {
  4357  	case core.NodeSelectorOpIn, core.NodeSelectorOpNotIn:
  4358  		if len(req.Values) != 1 {
  4359  			allErrs = append(allErrs, field.Required(fldPath.Child("values"),
  4360  				"must be only one value when `operator` is 'In' or 'NotIn' for node field selector"))
  4361  		}
  4362  	default:
  4363  		allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator, "not a valid selector operator"))
  4364  	}
  4365  
  4366  	if vf, found := nodeFieldSelectorValidators[req.Key]; !found {
  4367  		allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), req.Key, "not a valid field selector key"))
  4368  	} else {
  4369  		for i, v := range req.Values {
  4370  			for _, msg := range vf(v, false) {
  4371  				allErrs = append(allErrs, field.Invalid(fldPath.Child("values").Index(i), v, msg))
  4372  			}
  4373  		}
  4374  	}
  4375  
  4376  	return allErrs
  4377  }
  4378  
  4379  // ValidateNodeSelectorTerm tests that the specified node selector term has valid data
  4380  func ValidateNodeSelectorTerm(term core.NodeSelectorTerm, fldPath *field.Path) field.ErrorList {
  4381  	allErrs := field.ErrorList{}
  4382  
  4383  	for j, req := range term.MatchExpressions {
  4384  		allErrs = append(allErrs, ValidateNodeSelectorRequirement(req, fldPath.Child("matchExpressions").Index(j))...)
  4385  	}
  4386  
  4387  	for j, req := range term.MatchFields {
  4388  		allErrs = append(allErrs, ValidateNodeFieldSelectorRequirement(req, fldPath.Child("matchFields").Index(j))...)
  4389  	}
  4390  
  4391  	return allErrs
  4392  }
  4393  
  4394  // ValidateNodeSelector tests that the specified nodeSelector fields has valid data
  4395  func ValidateNodeSelector(nodeSelector *core.NodeSelector, fldPath *field.Path) field.ErrorList {
  4396  	allErrs := field.ErrorList{}
  4397  
  4398  	termFldPath := fldPath.Child("nodeSelectorTerms")
  4399  	if len(nodeSelector.NodeSelectorTerms) == 0 {
  4400  		return append(allErrs, field.Required(termFldPath, "must have at least one node selector term"))
  4401  	}
  4402  
  4403  	for i, term := range nodeSelector.NodeSelectorTerms {
  4404  		allErrs = append(allErrs, ValidateNodeSelectorTerm(term, termFldPath.Index(i))...)
  4405  	}
  4406  
  4407  	return allErrs
  4408  }
  4409  
  4410  // validateTopologySelectorLabelRequirement tests that the specified TopologySelectorLabelRequirement fields has valid data,
  4411  // and constructs a set containing all of its Values.
  4412  func validateTopologySelectorLabelRequirement(rq core.TopologySelectorLabelRequirement, fldPath *field.Path) (sets.Set[string], field.ErrorList) {
  4413  	allErrs := field.ErrorList{}
  4414  	valueSet := make(sets.Set[string])
  4415  	valuesPath := fldPath.Child("values")
  4416  	if len(rq.Values) == 0 {
  4417  		allErrs = append(allErrs, field.Required(valuesPath, ""))
  4418  	}
  4419  
  4420  	// Validate set property of Values field
  4421  	for i, value := range rq.Values {
  4422  		if valueSet.Has(value) {
  4423  			allErrs = append(allErrs, field.Duplicate(valuesPath.Index(i), value))
  4424  		}
  4425  		valueSet.Insert(value)
  4426  	}
  4427  
  4428  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(rq.Key, fldPath.Child("key"))...)
  4429  
  4430  	return valueSet, allErrs
  4431  }
  4432  
  4433  // ValidateTopologySelectorTerm tests that the specified topology selector term has valid data,
  4434  // and constructs a map representing the term in raw form.
  4435  func ValidateTopologySelectorTerm(term core.TopologySelectorTerm, fldPath *field.Path) (map[string]sets.Set[string], field.ErrorList) {
  4436  	allErrs := field.ErrorList{}
  4437  	exprMap := make(map[string]sets.Set[string])
  4438  	exprPath := fldPath.Child("matchLabelExpressions")
  4439  
  4440  	// Allow empty MatchLabelExpressions, in case this field becomes optional in the future.
  4441  	for i, req := range term.MatchLabelExpressions {
  4442  		idxPath := exprPath.Index(i)
  4443  		valueSet, exprErrs := validateTopologySelectorLabelRequirement(req, idxPath)
  4444  		allErrs = append(allErrs, exprErrs...)
  4445  
  4446  		// Validate no duplicate keys exist.
  4447  		if _, exists := exprMap[req.Key]; exists {
  4448  			allErrs = append(allErrs, field.Duplicate(idxPath.Child("key"), req.Key))
  4449  		}
  4450  		exprMap[req.Key] = valueSet
  4451  	}
  4452  
  4453  	return exprMap, allErrs
  4454  }
  4455  
  4456  // ValidateAvoidPodsInNodeAnnotations tests that the serialized AvoidPods in Node.Annotations has valid data
  4457  func ValidateAvoidPodsInNodeAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  4458  	allErrs := field.ErrorList{}
  4459  
  4460  	v1Avoids, err := schedulinghelper.GetAvoidPodsFromNodeAnnotations(annotations)
  4461  	if err != nil {
  4462  		allErrs = append(allErrs, field.Invalid(fldPath.Child("AvoidPods"), core.PreferAvoidPodsAnnotationKey, err.Error()))
  4463  		return allErrs
  4464  	}
  4465  	var avoids core.AvoidPods
  4466  	if err := corev1.Convert_v1_AvoidPods_To_core_AvoidPods(&v1Avoids, &avoids, nil); err != nil {
  4467  		allErrs = append(allErrs, field.Invalid(fldPath.Child("AvoidPods"), core.PreferAvoidPodsAnnotationKey, err.Error()))
  4468  		return allErrs
  4469  	}
  4470  
  4471  	if len(avoids.PreferAvoidPods) != 0 {
  4472  		for i, pa := range avoids.PreferAvoidPods {
  4473  			idxPath := fldPath.Child(core.PreferAvoidPodsAnnotationKey).Index(i)
  4474  			allErrs = append(allErrs, validatePreferAvoidPodsEntry(pa, idxPath)...)
  4475  		}
  4476  	}
  4477  
  4478  	return allErrs
  4479  }
  4480  
  4481  // validatePreferAvoidPodsEntry tests if given PreferAvoidPodsEntry has valid data.
  4482  func validatePreferAvoidPodsEntry(avoidPodEntry core.PreferAvoidPodsEntry, fldPath *field.Path) field.ErrorList {
  4483  	allErrors := field.ErrorList{}
  4484  	if avoidPodEntry.PodSignature.PodController == nil {
  4485  		allErrors = append(allErrors, field.Required(fldPath.Child("PodSignature"), ""))
  4486  	} else {
  4487  		if !*(avoidPodEntry.PodSignature.PodController.Controller) {
  4488  			allErrors = append(allErrors,
  4489  				field.Invalid(fldPath.Child("PodSignature").Child("PodController").Child("Controller"),
  4490  					*(avoidPodEntry.PodSignature.PodController.Controller), "must point to a controller"))
  4491  		}
  4492  	}
  4493  	return allErrors
  4494  }
  4495  
  4496  // ValidatePreferredSchedulingTerms tests that the specified SoftNodeAffinity fields has valid data
  4497  func ValidatePreferredSchedulingTerms(terms []core.PreferredSchedulingTerm, fldPath *field.Path) field.ErrorList {
  4498  	allErrs := field.ErrorList{}
  4499  
  4500  	for i, term := range terms {
  4501  		if term.Weight <= 0 || term.Weight > 100 {
  4502  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("weight"), term.Weight, "must be in the range 1-100"))
  4503  		}
  4504  
  4505  		allErrs = append(allErrs, ValidateNodeSelectorTerm(term.Preference, fldPath.Index(i).Child("preference"))...)
  4506  	}
  4507  	return allErrs
  4508  }
  4509  
  4510  // validatePodAffinityTerm tests that the specified podAffinityTerm fields have valid data
  4511  func validatePodAffinityTerm(podAffinityTerm core.PodAffinityTerm, allowInvalidLabelValueInSelector bool, fldPath *field.Path) field.ErrorList {
  4512  	allErrs := field.ErrorList{}
  4513  
  4514  	allErrs = append(allErrs, ValidatePodAffinityTermSelector(podAffinityTerm, allowInvalidLabelValueInSelector, fldPath)...)
  4515  	for _, name := range podAffinityTerm.Namespaces {
  4516  		for _, msg := range ValidateNamespaceName(name, false) {
  4517  			allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), name, msg))
  4518  		}
  4519  	}
  4520  	allErrs = append(allErrs, validateMatchLabelKeysAndMismatchLabelKeys(fldPath, podAffinityTerm.MatchLabelKeys, podAffinityTerm.MismatchLabelKeys, podAffinityTerm.LabelSelector)...)
  4521  	if len(podAffinityTerm.TopologyKey) == 0 {
  4522  		allErrs = append(allErrs, field.Required(fldPath.Child("topologyKey"), "can not be empty"))
  4523  	}
  4524  	return append(allErrs, unversionedvalidation.ValidateLabelName(podAffinityTerm.TopologyKey, fldPath.Child("topologyKey"))...)
  4525  }
  4526  
  4527  // validatePodAffinityTerms tests that the specified podAffinityTerms fields have valid data
  4528  func validatePodAffinityTerms(podAffinityTerms []core.PodAffinityTerm, allowInvalidLabelValueInSelector bool, fldPath *field.Path) field.ErrorList {
  4529  	allErrs := field.ErrorList{}
  4530  	for i, podAffinityTerm := range podAffinityTerms {
  4531  		allErrs = append(allErrs, validatePodAffinityTerm(podAffinityTerm, allowInvalidLabelValueInSelector, fldPath.Index(i))...)
  4532  	}
  4533  	return allErrs
  4534  }
  4535  
  4536  // validateWeightedPodAffinityTerms tests that the specified weightedPodAffinityTerms fields have valid data
  4537  func validateWeightedPodAffinityTerms(weightedPodAffinityTerms []core.WeightedPodAffinityTerm, allowInvalidLabelValueInSelector bool, fldPath *field.Path) field.ErrorList {
  4538  	allErrs := field.ErrorList{}
  4539  	for j, weightedTerm := range weightedPodAffinityTerms {
  4540  		if weightedTerm.Weight <= 0 || weightedTerm.Weight > 100 {
  4541  			allErrs = append(allErrs, field.Invalid(fldPath.Index(j).Child("weight"), weightedTerm.Weight, "must be in the range 1-100"))
  4542  		}
  4543  		allErrs = append(allErrs, validatePodAffinityTerm(weightedTerm.PodAffinityTerm, allowInvalidLabelValueInSelector, fldPath.Index(j).Child("podAffinityTerm"))...)
  4544  	}
  4545  	return allErrs
  4546  }
  4547  
  4548  // validatePodAntiAffinity tests that the specified podAntiAffinity fields have valid data
  4549  func validatePodAntiAffinity(podAntiAffinity *core.PodAntiAffinity, allowInvalidLabelValueInSelector bool, fldPath *field.Path) field.ErrorList {
  4550  	allErrs := field.ErrorList{}
  4551  	// TODO:Uncomment below code once RequiredDuringSchedulingRequiredDuringExecution is implemented.
  4552  	// if podAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution != nil {
  4553  	//	allErrs = append(allErrs, validatePodAffinityTerms(podAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution, false,
  4554  	//		fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
  4555  	// }
  4556  	if podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  4557  		allErrs = append(allErrs, validatePodAffinityTerms(podAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution, allowInvalidLabelValueInSelector,
  4558  			fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
  4559  	}
  4560  	if podAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
  4561  		allErrs = append(allErrs, validateWeightedPodAffinityTerms(podAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, allowInvalidLabelValueInSelector,
  4562  			fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
  4563  	}
  4564  	return allErrs
  4565  }
  4566  
  4567  // validateNodeAffinity tests that the specified nodeAffinity fields have valid data
  4568  func validateNodeAffinity(na *core.NodeAffinity, fldPath *field.Path) field.ErrorList {
  4569  	allErrs := field.ErrorList{}
  4570  	// TODO: Uncomment the next three lines once RequiredDuringSchedulingRequiredDuringExecution is implemented.
  4571  	// if na.RequiredDuringSchedulingRequiredDuringExecution != nil {
  4572  	//	allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingRequiredDuringExecution, fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
  4573  	// }
  4574  	if na.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  4575  		allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingIgnoredDuringExecution, fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
  4576  	}
  4577  	if len(na.PreferredDuringSchedulingIgnoredDuringExecution) > 0 {
  4578  		allErrs = append(allErrs, ValidatePreferredSchedulingTerms(na.PreferredDuringSchedulingIgnoredDuringExecution, fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
  4579  	}
  4580  	return allErrs
  4581  }
  4582  
  4583  // validatePodAffinity tests that the specified podAffinity fields have valid data
  4584  func validatePodAffinity(podAffinity *core.PodAffinity, allowInvalidLabelValueInSelector bool, fldPath *field.Path) field.ErrorList {
  4585  	allErrs := field.ErrorList{}
  4586  	// TODO:Uncomment below code once RequiredDuringSchedulingRequiredDuringExecution is implemented.
  4587  	// if podAffinity.RequiredDuringSchedulingRequiredDuringExecution != nil {
  4588  	//	allErrs = append(allErrs, validatePodAffinityTerms(podAffinity.RequiredDuringSchedulingRequiredDuringExecution, false,
  4589  	//		fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
  4590  	// }
  4591  	if podAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  4592  		allErrs = append(allErrs, validatePodAffinityTerms(podAffinity.RequiredDuringSchedulingIgnoredDuringExecution, allowInvalidLabelValueInSelector,
  4593  			fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
  4594  	}
  4595  	if podAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
  4596  		allErrs = append(allErrs, validateWeightedPodAffinityTerms(podAffinity.PreferredDuringSchedulingIgnoredDuringExecution, allowInvalidLabelValueInSelector,
  4597  			fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
  4598  	}
  4599  	return allErrs
  4600  }
  4601  
  4602  func validateSeccompProfileField(sp *core.SeccompProfile, fldPath *field.Path) field.ErrorList {
  4603  	allErrs := field.ErrorList{}
  4604  	if sp == nil {
  4605  		return allErrs
  4606  	}
  4607  
  4608  	if err := validateSeccompProfileType(fldPath.Child("type"), sp.Type); err != nil {
  4609  		allErrs = append(allErrs, err)
  4610  	}
  4611  
  4612  	if sp.Type == core.SeccompProfileTypeLocalhost {
  4613  		if sp.LocalhostProfile == nil {
  4614  			allErrs = append(allErrs, field.Required(fldPath.Child("localhostProfile"), "must be set when seccomp type is Localhost"))
  4615  		} else {
  4616  			allErrs = append(allErrs, validateLocalDescendingPath(*sp.LocalhostProfile, fldPath.Child("localhostProfile"))...)
  4617  		}
  4618  	} else {
  4619  		if sp.LocalhostProfile != nil {
  4620  			allErrs = append(allErrs, field.Invalid(fldPath.Child("localhostProfile"), sp, "can only be set when seccomp type is Localhost"))
  4621  		}
  4622  	}
  4623  
  4624  	return allErrs
  4625  }
  4626  
  4627  func ValidateSeccompProfile(p string, fldPath *field.Path) field.ErrorList {
  4628  	if p == core.SeccompProfileRuntimeDefault || p == core.DeprecatedSeccompProfileDockerDefault {
  4629  		return nil
  4630  	}
  4631  	if p == v1.SeccompProfileNameUnconfined {
  4632  		return nil
  4633  	}
  4634  	if strings.HasPrefix(p, v1.SeccompLocalhostProfileNamePrefix) {
  4635  		return validateLocalDescendingPath(strings.TrimPrefix(p, v1.SeccompLocalhostProfileNamePrefix), fldPath)
  4636  	}
  4637  	return field.ErrorList{field.Invalid(fldPath, p, "must be a valid seccomp profile")}
  4638  }
  4639  
  4640  func ValidateSeccompPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  4641  	allErrs := field.ErrorList{}
  4642  	if p, exists := annotations[core.SeccompPodAnnotationKey]; exists {
  4643  		allErrs = append(allErrs, ValidateSeccompProfile(p, fldPath.Child(core.SeccompPodAnnotationKey))...)
  4644  	}
  4645  	for k, p := range annotations {
  4646  		if strings.HasPrefix(k, core.SeccompContainerAnnotationKeyPrefix) {
  4647  			allErrs = append(allErrs, ValidateSeccompProfile(p, fldPath.Child(k))...)
  4648  		}
  4649  	}
  4650  
  4651  	return allErrs
  4652  }
  4653  
  4654  // ValidateSeccompProfileType tests that the argument is a valid SeccompProfileType.
  4655  func validateSeccompProfileType(fldPath *field.Path, seccompProfileType core.SeccompProfileType) *field.Error {
  4656  	switch seccompProfileType {
  4657  	case core.SeccompProfileTypeLocalhost, core.SeccompProfileTypeRuntimeDefault, core.SeccompProfileTypeUnconfined:
  4658  		return nil
  4659  	case "":
  4660  		return field.Required(fldPath, "type is required when seccompProfile is set")
  4661  	default:
  4662  		return field.NotSupported(fldPath, seccompProfileType, []core.SeccompProfileType{core.SeccompProfileTypeLocalhost, core.SeccompProfileTypeRuntimeDefault, core.SeccompProfileTypeUnconfined})
  4663  	}
  4664  }
  4665  
  4666  func ValidateAppArmorPodAnnotations(annotations map[string]string, spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
  4667  	allErrs := field.ErrorList{}
  4668  	for k, p := range annotations {
  4669  		if !strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) {
  4670  			continue
  4671  		}
  4672  		containerName := strings.TrimPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix)
  4673  		if !podSpecHasContainer(spec, containerName) {
  4674  			allErrs = append(allErrs, field.Invalid(fldPath.Key(k), containerName, "container not found"))
  4675  		}
  4676  
  4677  		if err := ValidateAppArmorProfileFormat(p); err != nil {
  4678  			allErrs = append(allErrs, field.Invalid(fldPath.Key(k), p, err.Error()))
  4679  		}
  4680  	}
  4681  
  4682  	return allErrs
  4683  }
  4684  
  4685  func ValidateAppArmorProfileFormat(profile string) error {
  4686  	if profile == "" || profile == v1.AppArmorBetaProfileRuntimeDefault || profile == v1.AppArmorBetaProfileNameUnconfined {
  4687  		return nil
  4688  	}
  4689  	if !strings.HasPrefix(profile, v1.AppArmorBetaProfileNamePrefix) {
  4690  		return fmt.Errorf("invalid AppArmor profile name: %q", profile)
  4691  	}
  4692  	return nil
  4693  }
  4694  
  4695  func podSpecHasContainer(spec *core.PodSpec, containerName string) bool {
  4696  	var hasContainer bool
  4697  	podshelper.VisitContainersWithPath(spec, field.NewPath("spec"), func(c *core.Container, _ *field.Path) bool {
  4698  		if c.Name == containerName {
  4699  			hasContainer = true
  4700  			return false
  4701  		}
  4702  		return true
  4703  	})
  4704  	return hasContainer
  4705  }
  4706  
  4707  const (
  4708  	// a sysctl segment regex, concatenated with dots to form a sysctl name
  4709  	SysctlSegmentFmt string = "[a-z0-9]([-_a-z0-9]*[a-z0-9])?"
  4710  
  4711  	// a sysctl name regex with slash allowed
  4712  	SysctlContainSlashFmt string = "(" + SysctlSegmentFmt + "[\\./])*" + SysctlSegmentFmt
  4713  
  4714  	// the maximal length of a sysctl name
  4715  	SysctlMaxLength int = 253
  4716  )
  4717  
  4718  var sysctlContainSlashRegexp = regexp.MustCompile("^" + SysctlContainSlashFmt + "$")
  4719  
  4720  // IsValidSysctlName checks that the given string is a valid sysctl name,
  4721  // i.e. matches SysctlContainSlashFmt.
  4722  // More info:
  4723  //
  4724  //	https://man7.org/linux/man-pages/man8/sysctl.8.html
  4725  //	https://man7.org/linux/man-pages/man5/sysctl.d.5.html
  4726  func IsValidSysctlName(name string) bool {
  4727  	if len(name) > SysctlMaxLength {
  4728  		return false
  4729  	}
  4730  	return sysctlContainSlashRegexp.MatchString(name)
  4731  }
  4732  
  4733  func validateSysctls(securityContext *core.PodSecurityContext, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  4734  	allErrs := field.ErrorList{}
  4735  	names := make(map[string]struct{})
  4736  	for i, s := range securityContext.Sysctls {
  4737  		if len(s.Name) == 0 {
  4738  			allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("name"), ""))
  4739  		} else if !IsValidSysctlName(s.Name) {
  4740  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, fmt.Sprintf("must have at most %d characters and match regex %s", SysctlMaxLength, sysctlContainSlashRegexp)))
  4741  		} else if _, ok := names[s.Name]; ok {
  4742  			allErrs = append(allErrs, field.Duplicate(fldPath.Index(i).Child("name"), s.Name))
  4743  		}
  4744  		if !opts.AllowNamespacedSysctlsForHostNetAndHostIPC {
  4745  			err := ValidateHostSysctl(s.Name, securityContext, fldPath.Index(i).Child("name"))
  4746  			if err != nil {
  4747  				allErrs = append(allErrs, err)
  4748  			}
  4749  		}
  4750  		names[s.Name] = struct{}{}
  4751  	}
  4752  	return allErrs
  4753  }
  4754  
  4755  // ValidateHostSysctl will return error if namespaced sysctls is applied to pod sharing the respective namespaces with the host.
  4756  func ValidateHostSysctl(sysctl string, securityContext *core.PodSecurityContext, fldPath *field.Path) *field.Error {
  4757  	ns, _, _ := utilsysctl.GetNamespace(sysctl)
  4758  	switch {
  4759  	case securityContext.HostNetwork && ns == utilsysctl.NetNamespace:
  4760  		return field.Invalid(fldPath, sysctl, "may not be specified when 'hostNetwork' is true")
  4761  	case securityContext.HostIPC && ns == utilsysctl.IPCNamespace:
  4762  		return field.Invalid(fldPath, sysctl, "may not be specified when 'hostIPC' is true")
  4763  	}
  4764  	return nil
  4765  }
  4766  
  4767  // validatePodSpecSecurityContext verifies the SecurityContext of a PodSpec,
  4768  // whether that is defined in a Pod or in an embedded PodSpec (e.g. a
  4769  // Deployment's pod template).
  4770  func validatePodSpecSecurityContext(securityContext *core.PodSecurityContext, spec *core.PodSpec, specPath, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  4771  	allErrs := field.ErrorList{}
  4772  
  4773  	if securityContext != nil {
  4774  		if securityContext.FSGroup != nil {
  4775  			for _, msg := range validation.IsValidGroupID(*securityContext.FSGroup) {
  4776  				allErrs = append(allErrs, field.Invalid(fldPath.Child("fsGroup"), *(securityContext.FSGroup), msg))
  4777  			}
  4778  		}
  4779  		if securityContext.RunAsUser != nil {
  4780  			for _, msg := range validation.IsValidUserID(*securityContext.RunAsUser) {
  4781  				allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *(securityContext.RunAsUser), msg))
  4782  			}
  4783  		}
  4784  		if securityContext.RunAsGroup != nil {
  4785  			for _, msg := range validation.IsValidGroupID(*securityContext.RunAsGroup) {
  4786  				allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsGroup"), *(securityContext.RunAsGroup), msg))
  4787  			}
  4788  		}
  4789  		for g, gid := range securityContext.SupplementalGroups {
  4790  			for _, msg := range validation.IsValidGroupID(gid) {
  4791  				allErrs = append(allErrs, field.Invalid(fldPath.Child("supplementalGroups").Index(g), gid, msg))
  4792  			}
  4793  		}
  4794  		if securityContext.ShareProcessNamespace != nil && securityContext.HostPID && *securityContext.ShareProcessNamespace {
  4795  			allErrs = append(allErrs, field.Invalid(fldPath.Child("shareProcessNamespace"), *securityContext.ShareProcessNamespace, "ShareProcessNamespace and HostPID cannot both be enabled"))
  4796  		}
  4797  
  4798  		if len(securityContext.Sysctls) != 0 {
  4799  			allErrs = append(allErrs, validateSysctls(securityContext, fldPath.Child("sysctls"), opts)...)
  4800  		}
  4801  
  4802  		if securityContext.FSGroupChangePolicy != nil {
  4803  			allErrs = append(allErrs, validateFSGroupChangePolicy(securityContext.FSGroupChangePolicy, fldPath.Child("fsGroupChangePolicy"))...)
  4804  		}
  4805  
  4806  		allErrs = append(allErrs, validateSeccompProfileField(securityContext.SeccompProfile, fldPath.Child("seccompProfile"))...)
  4807  		allErrs = append(allErrs, validateWindowsSecurityContextOptions(securityContext.WindowsOptions, fldPath.Child("windowsOptions"))...)
  4808  	}
  4809  
  4810  	return allErrs
  4811  }
  4812  
  4813  func ValidateContainerUpdates(newContainers, oldContainers []core.Container, fldPath *field.Path) (allErrs field.ErrorList, stop bool) {
  4814  	allErrs = field.ErrorList{}
  4815  	if len(newContainers) != len(oldContainers) {
  4816  		// TODO: Pinpoint the specific container that causes the invalid error after we have strategic merge diff
  4817  		allErrs = append(allErrs, field.Forbidden(fldPath, "pod updates may not add or remove containers"))
  4818  		return allErrs, true
  4819  	}
  4820  
  4821  	// validate updated container images
  4822  	for i, ctr := range newContainers {
  4823  		if len(ctr.Image) == 0 {
  4824  			allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("image"), ""))
  4825  		}
  4826  		// this is only called from ValidatePodUpdate so its safe to check leading/trailing whitespace.
  4827  		if len(strings.TrimSpace(ctr.Image)) != len(ctr.Image) {
  4828  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("image"), ctr.Image, "must not have leading or trailing whitespace"))
  4829  		}
  4830  	}
  4831  	return allErrs, false
  4832  }
  4833  
  4834  // ValidatePodCreate validates a pod in the context of its initial create
  4835  func ValidatePodCreate(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
  4836  	allErrs := validatePodMetadataAndSpec(pod, opts)
  4837  
  4838  	fldPath := field.NewPath("spec")
  4839  	// EphemeralContainers can only be set on update using the ephemeralcontainers subresource
  4840  	if len(pod.Spec.EphemeralContainers) > 0 {
  4841  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("ephemeralContainers"), "cannot be set on create"))
  4842  	}
  4843  	// A Pod cannot be assigned a Node if there are remaining scheduling gates.
  4844  	if pod.Spec.NodeName != "" && len(pod.Spec.SchedulingGates) != 0 {
  4845  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared"))
  4846  	}
  4847  	allErrs = append(allErrs, validateSeccompAnnotationsAndFields(pod.ObjectMeta, &pod.Spec, fldPath)...)
  4848  
  4849  	return allErrs
  4850  }
  4851  
  4852  // validateSeccompAnnotationsAndFields iterates through all containers and ensure that when both seccompProfile and seccomp annotations exist they match.
  4853  func validateSeccompAnnotationsAndFields(objectMeta metav1.ObjectMeta, podSpec *core.PodSpec, specPath *field.Path) field.ErrorList {
  4854  	allErrs := field.ErrorList{}
  4855  
  4856  	if podSpec.SecurityContext != nil && podSpec.SecurityContext.SeccompProfile != nil {
  4857  		// If both seccomp annotations and fields are specified, the values must match.
  4858  		if annotation, found := objectMeta.Annotations[v1.SeccompPodAnnotationKey]; found {
  4859  			seccompPath := specPath.Child("securityContext").Child("seccompProfile")
  4860  			err := validateSeccompAnnotationsAndFieldsMatch(annotation, podSpec.SecurityContext.SeccompProfile, seccompPath)
  4861  			if err != nil {
  4862  				allErrs = append(allErrs, err)
  4863  			}
  4864  		}
  4865  	}
  4866  
  4867  	podshelper.VisitContainersWithPath(podSpec, specPath, func(c *core.Container, cFldPath *field.Path) bool {
  4868  		var field *core.SeccompProfile
  4869  		if c.SecurityContext != nil {
  4870  			field = c.SecurityContext.SeccompProfile
  4871  		}
  4872  
  4873  		if field == nil {
  4874  			return true
  4875  		}
  4876  
  4877  		key := v1.SeccompContainerAnnotationKeyPrefix + c.Name
  4878  		if annotation, found := objectMeta.Annotations[key]; found {
  4879  			seccompPath := cFldPath.Child("securityContext").Child("seccompProfile")
  4880  			err := validateSeccompAnnotationsAndFieldsMatch(annotation, field, seccompPath)
  4881  			if err != nil {
  4882  				allErrs = append(allErrs, err)
  4883  			}
  4884  		}
  4885  		return true
  4886  	})
  4887  
  4888  	return allErrs
  4889  }
  4890  
  4891  func validateSeccompAnnotationsAndFieldsMatch(annotationValue string, seccompField *core.SeccompProfile, fldPath *field.Path) *field.Error {
  4892  	if seccompField == nil {
  4893  		return nil
  4894  	}
  4895  
  4896  	switch seccompField.Type {
  4897  	case core.SeccompProfileTypeUnconfined:
  4898  		if annotationValue != v1.SeccompProfileNameUnconfined {
  4899  			return field.Forbidden(fldPath.Child("type"), "seccomp type in annotation and field must match")
  4900  		}
  4901  
  4902  	case core.SeccompProfileTypeRuntimeDefault:
  4903  		if annotationValue != v1.SeccompProfileRuntimeDefault && annotationValue != v1.DeprecatedSeccompProfileDockerDefault {
  4904  			return field.Forbidden(fldPath.Child("type"), "seccomp type in annotation and field must match")
  4905  		}
  4906  
  4907  	case core.SeccompProfileTypeLocalhost:
  4908  		if !strings.HasPrefix(annotationValue, v1.SeccompLocalhostProfileNamePrefix) {
  4909  			return field.Forbidden(fldPath.Child("type"), "seccomp type in annotation and field must match")
  4910  		} else if seccompField.LocalhostProfile == nil || strings.TrimPrefix(annotationValue, v1.SeccompLocalhostProfileNamePrefix) != *seccompField.LocalhostProfile {
  4911  			return field.Forbidden(fldPath.Child("localhostProfile"), "seccomp profile in annotation and field must match")
  4912  		}
  4913  	}
  4914  
  4915  	return nil
  4916  }
  4917  
  4918  var updatablePodSpecFields = []string{
  4919  	"`spec.containers[*].image`",
  4920  	"`spec.initContainers[*].image`",
  4921  	"`spec.activeDeadlineSeconds`",
  4922  	"`spec.tolerations` (only additions to existing tolerations)",
  4923  	"`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)",
  4924  	"`spec.containers[*].resources` (for CPU/memory only)",
  4925  }
  4926  
  4927  // TODO(vinaykul,InPlacePodVerticalScaling): Drop this var once InPlacePodVerticalScaling goes GA and featuregate is gone.
  4928  var updatablePodSpecFieldsNoResources = []string{
  4929  	"`spec.containers[*].image`",
  4930  	"`spec.initContainers[*].image`",
  4931  	"`spec.activeDeadlineSeconds`",
  4932  	"`spec.tolerations` (only additions to existing tolerations)",
  4933  	"`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)",
  4934  }
  4935  
  4936  // ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
  4937  // that cannot be changed.
  4938  func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList {
  4939  	fldPath := field.NewPath("metadata")
  4940  	allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
  4941  	allErrs = append(allErrs, validatePodMetadataAndSpec(newPod, opts)...)
  4942  	allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"), opts)...)
  4943  	specPath := field.NewPath("spec")
  4944  
  4945  	// validate updateable fields:
  4946  	// 1.  spec.containers[*].image
  4947  	// 2.  spec.initContainers[*].image
  4948  	// 3.  spec.activeDeadlineSeconds
  4949  	// 4.  spec.terminationGracePeriodSeconds
  4950  	// 5.  spec.schedulingGates
  4951  
  4952  	containerErrs, stop := ValidateContainerUpdates(newPod.Spec.Containers, oldPod.Spec.Containers, specPath.Child("containers"))
  4953  	allErrs = append(allErrs, containerErrs...)
  4954  	if stop {
  4955  		return allErrs
  4956  	}
  4957  	containerErrs, stop = ValidateContainerUpdates(newPod.Spec.InitContainers, oldPod.Spec.InitContainers, specPath.Child("initContainers"))
  4958  	allErrs = append(allErrs, containerErrs...)
  4959  	if stop {
  4960  		return allErrs
  4961  	}
  4962  
  4963  	// validate updated spec.activeDeadlineSeconds.  two types of updates are allowed:
  4964  	// 1.  from nil to a positive value
  4965  	// 2.  from a positive value to a lesser, non-negative value
  4966  	if newPod.Spec.ActiveDeadlineSeconds != nil {
  4967  		newActiveDeadlineSeconds := *newPod.Spec.ActiveDeadlineSeconds
  4968  		if newActiveDeadlineSeconds < 0 || newActiveDeadlineSeconds > math.MaxInt32 {
  4969  			allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newActiveDeadlineSeconds, validation.InclusiveRangeError(0, math.MaxInt32)))
  4970  			return allErrs
  4971  		}
  4972  		if oldPod.Spec.ActiveDeadlineSeconds != nil {
  4973  			oldActiveDeadlineSeconds := *oldPod.Spec.ActiveDeadlineSeconds
  4974  			if oldActiveDeadlineSeconds < newActiveDeadlineSeconds {
  4975  				allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newActiveDeadlineSeconds, "must be less than or equal to previous value"))
  4976  				return allErrs
  4977  			}
  4978  		}
  4979  	} else if oldPod.Spec.ActiveDeadlineSeconds != nil {
  4980  		allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newPod.Spec.ActiveDeadlineSeconds, "must not update from a positive integer to nil value"))
  4981  	}
  4982  
  4983  	// Allow only additions to tolerations updates.
  4984  	allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...)
  4985  
  4986  	// Allow only deletions to schedulingGates updates.
  4987  	allErrs = append(allErrs, validateOnlyDeletedSchedulingGates(newPod.Spec.SchedulingGates, oldPod.Spec.SchedulingGates, specPath.Child("schedulingGates"))...)
  4988  
  4989  	// the last thing to check is pod spec equality.  If the pod specs are equal, then we can simply return the errors we have
  4990  	// so far and save the cost of a deep copy.
  4991  	if apiequality.Semantic.DeepEqual(newPod.Spec, oldPod.Spec) {
  4992  		return allErrs
  4993  	}
  4994  
  4995  	if qos.GetPodQOS(oldPod) != qos.ComputePodQOS(newPod) {
  4996  		allErrs = append(allErrs, field.Invalid(fldPath, newPod.Status.QOSClass, "Pod QoS is immutable"))
  4997  	}
  4998  
  4999  	// handle updateable fields by munging those fields prior to deep equal comparison.
  5000  	mungedPodSpec := *newPod.Spec.DeepCopy()
  5001  	// munge spec.containers[*].image
  5002  	var newContainers []core.Container
  5003  	for ix, container := range mungedPodSpec.Containers {
  5004  		container.Image = oldPod.Spec.Containers[ix].Image // +k8s:verify-mutation:reason=clone
  5005  		// When the feature-gate is turned off, any new requests attempting to update CPU or memory
  5006  		// resource values will result in validation failure.
  5007  		if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
  5008  			// Resources are mutable for CPU & memory only
  5009  			//   - user can now modify Resources to express new desired Resources
  5010  			mungeCpuMemResources := func(resourceList, oldResourceList core.ResourceList) core.ResourceList {
  5011  				if oldResourceList == nil {
  5012  					return nil
  5013  				}
  5014  				var mungedResourceList core.ResourceList
  5015  				if resourceList == nil {
  5016  					mungedResourceList = make(core.ResourceList)
  5017  				} else {
  5018  					mungedResourceList = resourceList.DeepCopy()
  5019  				}
  5020  				delete(mungedResourceList, core.ResourceCPU)
  5021  				delete(mungedResourceList, core.ResourceMemory)
  5022  				if cpu, found := oldResourceList[core.ResourceCPU]; found {
  5023  					mungedResourceList[core.ResourceCPU] = cpu
  5024  				}
  5025  				if mem, found := oldResourceList[core.ResourceMemory]; found {
  5026  					mungedResourceList[core.ResourceMemory] = mem
  5027  				}
  5028  				return mungedResourceList
  5029  			}
  5030  			lim := mungeCpuMemResources(container.Resources.Limits, oldPod.Spec.Containers[ix].Resources.Limits)
  5031  			req := mungeCpuMemResources(container.Resources.Requests, oldPod.Spec.Containers[ix].Resources.Requests)
  5032  			container.Resources = core.ResourceRequirements{Limits: lim, Requests: req}
  5033  		}
  5034  		newContainers = append(newContainers, container)
  5035  	}
  5036  	mungedPodSpec.Containers = newContainers
  5037  	// munge spec.initContainers[*].image
  5038  	var newInitContainers []core.Container
  5039  	for ix, container := range mungedPodSpec.InitContainers {
  5040  		container.Image = oldPod.Spec.InitContainers[ix].Image // +k8s:verify-mutation:reason=clone
  5041  		newInitContainers = append(newInitContainers, container)
  5042  	}
  5043  	mungedPodSpec.InitContainers = newInitContainers
  5044  	// munge spec.activeDeadlineSeconds
  5045  	mungedPodSpec.ActiveDeadlineSeconds = nil
  5046  	if oldPod.Spec.ActiveDeadlineSeconds != nil {
  5047  		activeDeadlineSeconds := *oldPod.Spec.ActiveDeadlineSeconds
  5048  		mungedPodSpec.ActiveDeadlineSeconds = &activeDeadlineSeconds
  5049  	}
  5050  	// munge spec.schedulingGates
  5051  	mungedPodSpec.SchedulingGates = oldPod.Spec.SchedulingGates // +k8s:verify-mutation:reason=clone
  5052  	// tolerations are checked before the deep copy, so munge those too
  5053  	mungedPodSpec.Tolerations = oldPod.Spec.Tolerations // +k8s:verify-mutation:reason=clone
  5054  
  5055  	// Relax validation of immutable fields to allow it to be set to 1 if it was previously negative.
  5056  	if oldPod.Spec.TerminationGracePeriodSeconds != nil && *oldPod.Spec.TerminationGracePeriodSeconds < 0 &&
  5057  		mungedPodSpec.TerminationGracePeriodSeconds != nil && *mungedPodSpec.TerminationGracePeriodSeconds == 1 {
  5058  		mungedPodSpec.TerminationGracePeriodSeconds = oldPod.Spec.TerminationGracePeriodSeconds // +k8s:verify-mutation:reason=clone
  5059  	}
  5060  
  5061  	// Handle validations specific to gated pods.
  5062  	podIsGated := len(oldPod.Spec.SchedulingGates) > 0
  5063  	if opts.AllowMutableNodeSelectorAndNodeAffinity && podIsGated {
  5064  		// Additions to spec.nodeSelector are allowed (no deletions or mutations) for gated pods.
  5065  		if !apiequality.Semantic.DeepEqual(mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector) {
  5066  			allErrs = append(allErrs, validateNodeSelectorMutation(specPath.Child("nodeSelector"), mungedPodSpec.NodeSelector, oldPod.Spec.NodeSelector)...)
  5067  			mungedPodSpec.NodeSelector = oldPod.Spec.NodeSelector // +k8s:verify-mutation:reason=clone
  5068  		}
  5069  
  5070  		// Validate node affinity mutations.
  5071  		var oldNodeAffinity *core.NodeAffinity
  5072  		if oldPod.Spec.Affinity != nil {
  5073  			oldNodeAffinity = oldPod.Spec.Affinity.NodeAffinity // +k8s:verify-mutation:reason=clone
  5074  		}
  5075  
  5076  		var mungedNodeAffinity *core.NodeAffinity
  5077  		if mungedPodSpec.Affinity != nil {
  5078  			mungedNodeAffinity = mungedPodSpec.Affinity.NodeAffinity // +k8s:verify-mutation:reason=clone
  5079  		}
  5080  
  5081  		if !apiequality.Semantic.DeepEqual(oldNodeAffinity, mungedNodeAffinity) {
  5082  			allErrs = append(allErrs, validateNodeAffinityMutation(specPath.Child("affinity").Child("nodeAffinity"), mungedNodeAffinity, oldNodeAffinity)...)
  5083  			switch {
  5084  			case mungedPodSpec.Affinity == nil && oldNodeAffinity == nil:
  5085  				// already effectively nil, no change needed
  5086  			case mungedPodSpec.Affinity == nil && oldNodeAffinity != nil:
  5087  				mungedPodSpec.Affinity = &core.Affinity{NodeAffinity: oldNodeAffinity} // +k8s:verify-mutation:reason=clone
  5088  			case mungedPodSpec.Affinity != nil && oldPod.Spec.Affinity == nil &&
  5089  				mungedPodSpec.Affinity.PodAntiAffinity == nil && mungedPodSpec.Affinity.PodAffinity == nil:
  5090  				// We ensure no other fields are being changed, but the NodeAffinity. If that's the case, and the
  5091  				// old pod's affinity is nil, we set the mungedPodSpec's affinity to nil.
  5092  				mungedPodSpec.Affinity = nil // +k8s:verify-mutation:reason=clone
  5093  			default:
  5094  				// The node affinity is being updated and the old pod Affinity is not nil.
  5095  				// We set the mungedPodSpec's node affinity to the old pod's node affinity.
  5096  				mungedPodSpec.Affinity.NodeAffinity = oldNodeAffinity // +k8s:verify-mutation:reason=clone
  5097  			}
  5098  		}
  5099  
  5100  		// Note: Unlike NodeAffinity and NodeSelector, we cannot make PodAffinity/PodAntiAffinity mutable due to the presence of the matchLabelKeys/mismatchLabelKeys feature.
  5101  		// Those features automatically generate the matchExpressions in labelSelector for PodAffinity/PodAntiAffinity when the Pod is created.
  5102  		// When we make them mutable, we need to make sure things like how to handle/validate matchLabelKeys,
  5103  		// and what if the fieldManager/A sets matchexpressions and fieldManager/B sets matchLabelKeys later. (could it lead the understandable conflict, etc)
  5104  	}
  5105  
  5106  	if !apiequality.Semantic.DeepEqual(mungedPodSpec, oldPod.Spec) {
  5107  		// This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is".
  5108  		// TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
  5109  		specDiff := cmp.Diff(oldPod.Spec, mungedPodSpec)
  5110  		errs := field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than %s\n%v", strings.Join(updatablePodSpecFieldsNoResources, ","), specDiff))
  5111  		if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
  5112  			errs = field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than %s\n%v", strings.Join(updatablePodSpecFields, ","), specDiff))
  5113  		}
  5114  		allErrs = append(allErrs, errs)
  5115  	}
  5116  	return allErrs
  5117  }
  5118  
  5119  // ValidateContainerStateTransition test to if any illegal container state transitions are being attempted
  5120  func ValidateContainerStateTransition(newStatuses, oldStatuses []core.ContainerStatus, fldpath *field.Path, restartPolicy core.RestartPolicy) field.ErrorList {
  5121  	allErrs := field.ErrorList{}
  5122  	// If we should always restart, containers are allowed to leave the terminated state
  5123  	if restartPolicy == core.RestartPolicyAlways {
  5124  		return allErrs
  5125  	}
  5126  	for i, oldStatus := range oldStatuses {
  5127  		// Skip any container that is not terminated
  5128  		if oldStatus.State.Terminated == nil {
  5129  			continue
  5130  		}
  5131  		// Skip any container that failed but is allowed to restart
  5132  		if oldStatus.State.Terminated.ExitCode != 0 && restartPolicy == core.RestartPolicyOnFailure {
  5133  			continue
  5134  		}
  5135  		for _, newStatus := range newStatuses {
  5136  			if oldStatus.Name == newStatus.Name && newStatus.State.Terminated == nil {
  5137  				allErrs = append(allErrs, field.Forbidden(fldpath.Index(i).Child("state"), "may not be transitioned to non-terminated state"))
  5138  			}
  5139  		}
  5140  	}
  5141  	return allErrs
  5142  }
  5143  
  5144  // ValidateInitContainerStateTransition test to if any illegal init container state transitions are being attempted
  5145  func ValidateInitContainerStateTransition(newStatuses, oldStatuses []core.ContainerStatus, fldpath *field.Path, podSpec *core.PodSpec) field.ErrorList {
  5146  	allErrs := field.ErrorList{}
  5147  	// If we should always restart, containers are allowed to leave the terminated state
  5148  	if podSpec.RestartPolicy == core.RestartPolicyAlways {
  5149  		return allErrs
  5150  	}
  5151  	for i, oldStatus := range oldStatuses {
  5152  		// Skip any container that is not terminated
  5153  		if oldStatus.State.Terminated == nil {
  5154  			continue
  5155  		}
  5156  		// Skip any container that failed but is allowed to restart
  5157  		if oldStatus.State.Terminated.ExitCode != 0 && podSpec.RestartPolicy == core.RestartPolicyOnFailure {
  5158  			continue
  5159  		}
  5160  
  5161  		// Skip any restartable init container that is allowed to restart
  5162  		isRestartableInitContainer := false
  5163  		for _, c := range podSpec.InitContainers {
  5164  			if oldStatus.Name == c.Name {
  5165  				if c.RestartPolicy != nil && *c.RestartPolicy == core.ContainerRestartPolicyAlways {
  5166  					isRestartableInitContainer = true
  5167  				}
  5168  				break
  5169  			}
  5170  		}
  5171  		if isRestartableInitContainer {
  5172  			continue
  5173  		}
  5174  
  5175  		for _, newStatus := range newStatuses {
  5176  			if oldStatus.Name == newStatus.Name && newStatus.State.Terminated == nil {
  5177  				allErrs = append(allErrs, field.Forbidden(fldpath.Index(i).Child("state"), "may not be transitioned to non-terminated state"))
  5178  			}
  5179  		}
  5180  	}
  5181  	return allErrs
  5182  }
  5183  
  5184  // ValidatePodStatusUpdate checks for changes to status that shouldn't occur in normal operation.
  5185  func ValidatePodStatusUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList {
  5186  	fldPath := field.NewPath("metadata")
  5187  	allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
  5188  	allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"), opts)...)
  5189  	allErrs = append(allErrs, validatePodConditions(newPod.Status.Conditions, fldPath.Child("conditions"))...)
  5190  
  5191  	fldPath = field.NewPath("status")
  5192  	if newPod.Spec.NodeName != oldPod.Spec.NodeName {
  5193  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("nodeName"), "may not be changed directly"))
  5194  	}
  5195  
  5196  	if newPod.Status.NominatedNodeName != oldPod.Status.NominatedNodeName && len(newPod.Status.NominatedNodeName) > 0 {
  5197  		for _, msg := range ValidateNodeName(newPod.Status.NominatedNodeName, false) {
  5198  			allErrs = append(allErrs, field.Invalid(fldPath.Child("nominatedNodeName"), newPod.Status.NominatedNodeName, msg))
  5199  		}
  5200  	}
  5201  
  5202  	// If pod should not restart, make sure the status update does not transition
  5203  	// any terminated containers to a non-terminated state.
  5204  	allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec.RestartPolicy)...)
  5205  	allErrs = append(allErrs, ValidateInitContainerStateTransition(newPod.Status.InitContainerStatuses, oldPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), &oldPod.Spec)...)
  5206  	// The kubelet will never restart ephemeral containers, so treat them like they have an implicit RestartPolicyNever.
  5207  	allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.EphemeralContainerStatuses, oldPod.Status.EphemeralContainerStatuses, fldPath.Child("ephemeralContainerStatuses"), core.RestartPolicyNever)...)
  5208  	allErrs = append(allErrs, validatePodResourceClaimStatuses(newPod.Status.ResourceClaimStatuses, newPod.Spec.ResourceClaims, fldPath.Child("resourceClaimStatuses"))...)
  5209  
  5210  	if newIPErrs := validatePodIPs(newPod); len(newIPErrs) > 0 {
  5211  		allErrs = append(allErrs, newIPErrs...)
  5212  	}
  5213  
  5214  	if newIPErrs := validateHostIPs(newPod); len(newIPErrs) > 0 {
  5215  		allErrs = append(allErrs, newIPErrs...)
  5216  	}
  5217  
  5218  	return allErrs
  5219  }
  5220  
  5221  // validatePodConditions tests if the custom pod conditions are valid.
  5222  func validatePodConditions(conditions []core.PodCondition, fldPath *field.Path) field.ErrorList {
  5223  	allErrs := field.ErrorList{}
  5224  	systemConditions := sets.New(
  5225  		core.PodScheduled,
  5226  		core.PodReady,
  5227  		core.PodInitialized)
  5228  	for i, condition := range conditions {
  5229  		if systemConditions.Has(condition.Type) {
  5230  			continue
  5231  		}
  5232  		allErrs = append(allErrs, ValidateQualifiedName(string(condition.Type), fldPath.Index(i).Child("Type"))...)
  5233  	}
  5234  	return allErrs
  5235  }
  5236  
  5237  // validatePodResourceClaimStatuses validates the ResourceClaimStatuses slice in a pod status.
  5238  func validatePodResourceClaimStatuses(statuses []core.PodResourceClaimStatus, podClaims []core.PodResourceClaim, fldPath *field.Path) field.ErrorList {
  5239  	var allErrs field.ErrorList
  5240  
  5241  	claimNames := sets.New[string]()
  5242  	for i, status := range statuses {
  5243  		idxPath := fldPath.Index(i)
  5244  		// There's no need to check the content of the name. If it matches an entry,
  5245  		// then it is valid, otherwise we reject it here.
  5246  		if !havePodClaim(podClaims, status.Name) {
  5247  			allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), status.Name, "must match the name of an entry in `spec.resourceClaims`"))
  5248  		}
  5249  		if claimNames.Has(status.Name) {
  5250  			allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), status.Name))
  5251  		} else {
  5252  			claimNames.Insert(status.Name)
  5253  		}
  5254  		if status.ResourceClaimName != nil {
  5255  			for _, detail := range ValidateResourceClaimName(*status.ResourceClaimName, false) {
  5256  				allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), status.ResourceClaimName, detail))
  5257  			}
  5258  		}
  5259  	}
  5260  
  5261  	return allErrs
  5262  }
  5263  
  5264  func havePodClaim(podClaims []core.PodResourceClaim, name string) bool {
  5265  	for _, podClaim := range podClaims {
  5266  		if podClaim.Name == name {
  5267  			return true
  5268  		}
  5269  	}
  5270  	return false
  5271  }
  5272  
  5273  // ValidatePodEphemeralContainersUpdate tests that a user update to EphemeralContainers is valid.
  5274  // newPod and oldPod must only differ in their EphemeralContainers.
  5275  func ValidatePodEphemeralContainersUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList {
  5276  	// Part 1: Validate newPod's spec and updates to metadata
  5277  	fldPath := field.NewPath("metadata")
  5278  	allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
  5279  	allErrs = append(allErrs, validatePodMetadataAndSpec(newPod, opts)...)
  5280  	allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"), opts)...)
  5281  
  5282  	// static pods don't support ephemeral containers #113935
  5283  	if _, ok := oldPod.Annotations[core.MirrorPodAnnotationKey]; ok {
  5284  		return field.ErrorList{field.Forbidden(field.NewPath(""), "static pods do not support ephemeral containers")}
  5285  	}
  5286  
  5287  	// Part 2: Validate that the changes between oldPod.Spec.EphemeralContainers and
  5288  	// newPod.Spec.EphemeralContainers are allowed.
  5289  	//
  5290  	// Existing EphemeralContainers may not be changed. Order isn't preserved by patch, so check each individually.
  5291  	newContainerIndex := make(map[string]*core.EphemeralContainer)
  5292  	specPath := field.NewPath("spec").Child("ephemeralContainers")
  5293  	for i := range newPod.Spec.EphemeralContainers {
  5294  		newContainerIndex[newPod.Spec.EphemeralContainers[i].Name] = &newPod.Spec.EphemeralContainers[i]
  5295  	}
  5296  	for _, old := range oldPod.Spec.EphemeralContainers {
  5297  		if new, ok := newContainerIndex[old.Name]; !ok {
  5298  			allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("existing ephemeral containers %q may not be removed\n", old.Name)))
  5299  		} else if !apiequality.Semantic.DeepEqual(old, *new) {
  5300  			specDiff := cmp.Diff(old, *new)
  5301  			allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("existing ephemeral containers %q may not be changed\n%v", old.Name, specDiff)))
  5302  		}
  5303  	}
  5304  
  5305  	return allErrs
  5306  }
  5307  
  5308  // ValidatePodBinding tests if required fields in the pod binding are legal.
  5309  func ValidatePodBinding(binding *core.Binding) field.ErrorList {
  5310  	allErrs := field.ErrorList{}
  5311  
  5312  	if len(binding.Target.Kind) != 0 && binding.Target.Kind != "Node" {
  5313  		// TODO: When validation becomes versioned, this gets more complicated.
  5314  		allErrs = append(allErrs, field.NotSupported(field.NewPath("target", "kind"), binding.Target.Kind, []string{"Node", "<empty>"}))
  5315  	}
  5316  	if len(binding.Target.Name) == 0 {
  5317  		// TODO: When validation becomes versioned, this gets more complicated.
  5318  		allErrs = append(allErrs, field.Required(field.NewPath("target", "name"), ""))
  5319  	}
  5320  
  5321  	return allErrs
  5322  }
  5323  
  5324  // ValidatePodTemplate tests if required fields in the pod template are set.
  5325  func ValidatePodTemplate(pod *core.PodTemplate, opts PodValidationOptions) field.ErrorList {
  5326  	allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, field.NewPath("metadata"))
  5327  	allErrs = append(allErrs, ValidatePodTemplateSpec(&pod.Template, field.NewPath("template"), opts)...)
  5328  	return allErrs
  5329  }
  5330  
  5331  // ValidatePodTemplateUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
  5332  // that cannot be changed.
  5333  func ValidatePodTemplateUpdate(newPod, oldPod *core.PodTemplate, opts PodValidationOptions) field.ErrorList {
  5334  	allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, field.NewPath("metadata"))
  5335  	allErrs = append(allErrs, ValidatePodTemplateSpec(&newPod.Template, field.NewPath("template"), opts)...)
  5336  	return allErrs
  5337  }
  5338  
  5339  var supportedSessionAffinityType = sets.New(core.ServiceAffinityClientIP, core.ServiceAffinityNone)
  5340  var supportedServiceType = sets.New(core.ServiceTypeClusterIP, core.ServiceTypeNodePort,
  5341  	core.ServiceTypeLoadBalancer, core.ServiceTypeExternalName)
  5342  
  5343  var supportedServiceInternalTrafficPolicy = sets.New(core.ServiceInternalTrafficPolicyCluster, core.ServiceInternalTrafficPolicyLocal)
  5344  
  5345  var supportedServiceIPFamily = sets.New(core.IPv4Protocol, core.IPv6Protocol)
  5346  var supportedServiceIPFamilyPolicy = sets.New(
  5347  	core.IPFamilyPolicySingleStack,
  5348  	core.IPFamilyPolicyPreferDualStack,
  5349  	core.IPFamilyPolicyRequireDualStack)
  5350  
  5351  // ValidateService tests if required fields/annotations of a Service are valid.
  5352  func ValidateService(service *core.Service) field.ErrorList {
  5353  	metaPath := field.NewPath("metadata")
  5354  	allErrs := ValidateObjectMeta(&service.ObjectMeta, true, ValidateServiceName, metaPath)
  5355  
  5356  	topologyHintsVal, topologyHintsSet := service.Annotations[core.DeprecatedAnnotationTopologyAwareHints]
  5357  	topologyModeVal, topologyModeSet := service.Annotations[core.AnnotationTopologyMode]
  5358  
  5359  	if topologyModeSet && topologyHintsSet && topologyModeVal != topologyHintsVal {
  5360  		message := fmt.Sprintf("must match annotations[%s] when both are specified", core.DeprecatedAnnotationTopologyAwareHints)
  5361  		allErrs = append(allErrs, field.Invalid(metaPath.Child("annotations").Key(core.AnnotationTopologyMode), topologyModeVal, message))
  5362  	}
  5363  
  5364  	specPath := field.NewPath("spec")
  5365  
  5366  	if len(service.Spec.Ports) == 0 && !isHeadlessService(service) && service.Spec.Type != core.ServiceTypeExternalName {
  5367  		allErrs = append(allErrs, field.Required(specPath.Child("ports"), ""))
  5368  	}
  5369  	switch service.Spec.Type {
  5370  	case core.ServiceTypeLoadBalancer:
  5371  		for ix := range service.Spec.Ports {
  5372  			port := &service.Spec.Ports[ix]
  5373  			// This is a workaround for broken cloud environments that
  5374  			// over-open firewalls.  Hopefully it can go away when more clouds
  5375  			// understand containers better.
  5376  			if port.Port == ports.KubeletPort {
  5377  				portPath := specPath.Child("ports").Index(ix)
  5378  				allErrs = append(allErrs, field.Invalid(portPath, port.Port, fmt.Sprintf("may not expose port %v externally since it is used by kubelet", ports.KubeletPort)))
  5379  			}
  5380  		}
  5381  		if isHeadlessService(service) {
  5382  			allErrs = append(allErrs, field.Invalid(specPath.Child("clusterIPs").Index(0), service.Spec.ClusterIPs[0], "may not be set to 'None' for LoadBalancer services"))
  5383  		}
  5384  	case core.ServiceTypeNodePort:
  5385  		if isHeadlessService(service) {
  5386  			allErrs = append(allErrs, field.Invalid(specPath.Child("clusterIPs").Index(0), service.Spec.ClusterIPs[0], "may not be set to 'None' for NodePort services"))
  5387  		}
  5388  	case core.ServiceTypeExternalName:
  5389  		// must have  len(.spec.ClusterIPs) == 0 // note: strategy sets ClusterIPs based on ClusterIP
  5390  		if len(service.Spec.ClusterIPs) > 0 {
  5391  			allErrs = append(allErrs, field.Forbidden(specPath.Child("clusterIPs"), "may not be set for ExternalName services"))
  5392  		}
  5393  
  5394  		// must have nil families and nil policy
  5395  		if len(service.Spec.IPFamilies) > 0 {
  5396  			allErrs = append(allErrs, field.Forbidden(specPath.Child("ipFamilies"), "may not be set for ExternalName services"))
  5397  		}
  5398  		if service.Spec.IPFamilyPolicy != nil {
  5399  			allErrs = append(allErrs, field.Forbidden(specPath.Child("ipFamilyPolicy"), "may not be set for ExternalName services"))
  5400  		}
  5401  
  5402  		// The value (a CNAME) may have a trailing dot to denote it as fully qualified
  5403  		cname := strings.TrimSuffix(service.Spec.ExternalName, ".")
  5404  		if len(cname) > 0 {
  5405  			allErrs = append(allErrs, ValidateDNS1123Subdomain(cname, specPath.Child("externalName"))...)
  5406  		} else {
  5407  			allErrs = append(allErrs, field.Required(specPath.Child("externalName"), ""))
  5408  		}
  5409  	}
  5410  
  5411  	allPortNames := sets.Set[string]{}
  5412  	portsPath := specPath.Child("ports")
  5413  	for i := range service.Spec.Ports {
  5414  		portPath := portsPath.Index(i)
  5415  		allErrs = append(allErrs, validateServicePort(&service.Spec.Ports[i], len(service.Spec.Ports) > 1, isHeadlessService(service), &allPortNames, portPath)...)
  5416  	}
  5417  
  5418  	if service.Spec.Selector != nil {
  5419  		allErrs = append(allErrs, unversionedvalidation.ValidateLabels(service.Spec.Selector, specPath.Child("selector"))...)
  5420  	}
  5421  
  5422  	if len(service.Spec.SessionAffinity) == 0 {
  5423  		allErrs = append(allErrs, field.Required(specPath.Child("sessionAffinity"), ""))
  5424  	} else if !supportedSessionAffinityType.Has(service.Spec.SessionAffinity) {
  5425  		allErrs = append(allErrs, field.NotSupported(specPath.Child("sessionAffinity"), service.Spec.SessionAffinity, sets.List(supportedSessionAffinityType)))
  5426  	}
  5427  
  5428  	if service.Spec.SessionAffinity == core.ServiceAffinityClientIP {
  5429  		allErrs = append(allErrs, validateClientIPAffinityConfig(service.Spec.SessionAffinityConfig, specPath.Child("sessionAffinityConfig"))...)
  5430  	} else if service.Spec.SessionAffinity == core.ServiceAffinityNone {
  5431  		if service.Spec.SessionAffinityConfig != nil {
  5432  			allErrs = append(allErrs, field.Forbidden(specPath.Child("sessionAffinityConfig"), fmt.Sprintf("must not be set when session affinity is %s", core.ServiceAffinityNone)))
  5433  		}
  5434  	}
  5435  
  5436  	// dualstack <-> ClusterIPs <-> ipfamilies
  5437  	allErrs = append(allErrs, ValidateServiceClusterIPsRelatedFields(service)...)
  5438  
  5439  	ipPath := specPath.Child("externalIPs")
  5440  	for i, ip := range service.Spec.ExternalIPs {
  5441  		idxPath := ipPath.Index(i)
  5442  		if msgs := validation.IsValidIP(ip); len(msgs) != 0 {
  5443  			for i := range msgs {
  5444  				allErrs = append(allErrs, field.Invalid(idxPath, ip, msgs[i]))
  5445  			}
  5446  		} else {
  5447  			allErrs = append(allErrs, ValidateNonSpecialIP(ip, idxPath)...)
  5448  		}
  5449  	}
  5450  
  5451  	if len(service.Spec.Type) == 0 {
  5452  		allErrs = append(allErrs, field.Required(specPath.Child("type"), ""))
  5453  	} else if !supportedServiceType.Has(service.Spec.Type) {
  5454  		allErrs = append(allErrs, field.NotSupported(specPath.Child("type"), service.Spec.Type, sets.List(supportedServiceType)))
  5455  	}
  5456  
  5457  	if service.Spec.Type == core.ServiceTypeClusterIP {
  5458  		portsPath := specPath.Child("ports")
  5459  		for i := range service.Spec.Ports {
  5460  			portPath := portsPath.Index(i)
  5461  			if service.Spec.Ports[i].NodePort != 0 {
  5462  				allErrs = append(allErrs, field.Forbidden(portPath.Child("nodePort"), "may not be used when `type` is 'ClusterIP'"))
  5463  			}
  5464  		}
  5465  	}
  5466  
  5467  	// Check for duplicate NodePorts, considering (protocol,port) pairs
  5468  	portsPath = specPath.Child("ports")
  5469  	nodePorts := make(map[core.ServicePort]bool)
  5470  	for i := range service.Spec.Ports {
  5471  		port := &service.Spec.Ports[i]
  5472  		if port.NodePort == 0 {
  5473  			continue
  5474  		}
  5475  		portPath := portsPath.Index(i)
  5476  		var key core.ServicePort
  5477  		key.Protocol = port.Protocol
  5478  		key.NodePort = port.NodePort
  5479  		_, found := nodePorts[key]
  5480  		if found {
  5481  			allErrs = append(allErrs, field.Duplicate(portPath.Child("nodePort"), port.NodePort))
  5482  		}
  5483  		nodePorts[key] = true
  5484  	}
  5485  
  5486  	// Check for duplicate Ports, considering (protocol,port) pairs
  5487  	portsPath = specPath.Child("ports")
  5488  	ports := make(map[core.ServicePort]bool)
  5489  	for i, port := range service.Spec.Ports {
  5490  		portPath := portsPath.Index(i)
  5491  		key := core.ServicePort{Protocol: port.Protocol, Port: port.Port}
  5492  		_, found := ports[key]
  5493  		if found {
  5494  			allErrs = append(allErrs, field.Duplicate(portPath, key))
  5495  		}
  5496  		ports[key] = true
  5497  	}
  5498  
  5499  	// Validate SourceRange field and annotation
  5500  	_, ok := service.Annotations[core.AnnotationLoadBalancerSourceRangesKey]
  5501  	if len(service.Spec.LoadBalancerSourceRanges) > 0 || ok {
  5502  		var fieldPath *field.Path
  5503  		var val string
  5504  		if len(service.Spec.LoadBalancerSourceRanges) > 0 {
  5505  			fieldPath = specPath.Child("LoadBalancerSourceRanges")
  5506  			val = fmt.Sprintf("%v", service.Spec.LoadBalancerSourceRanges)
  5507  		} else {
  5508  			fieldPath = field.NewPath("metadata", "annotations").Key(core.AnnotationLoadBalancerSourceRangesKey)
  5509  			val = service.Annotations[core.AnnotationLoadBalancerSourceRangesKey]
  5510  		}
  5511  		if service.Spec.Type != core.ServiceTypeLoadBalancer {
  5512  			allErrs = append(allErrs, field.Forbidden(fieldPath, "may only be used when `type` is 'LoadBalancer'"))
  5513  		}
  5514  		_, err := apiservice.GetLoadBalancerSourceRanges(service)
  5515  		if err != nil {
  5516  			allErrs = append(allErrs, field.Invalid(fieldPath, val, "must be a list of IP ranges. For example, 10.240.0.0/24,10.250.0.0/24 "))
  5517  		}
  5518  	}
  5519  
  5520  	if service.Spec.AllocateLoadBalancerNodePorts != nil && service.Spec.Type != core.ServiceTypeLoadBalancer {
  5521  		allErrs = append(allErrs, field.Forbidden(specPath.Child("allocateLoadBalancerNodePorts"), "may only be used when `type` is 'LoadBalancer'"))
  5522  	}
  5523  
  5524  	if service.Spec.Type == core.ServiceTypeLoadBalancer && service.Spec.AllocateLoadBalancerNodePorts == nil {
  5525  		allErrs = append(allErrs, field.Required(field.NewPath("allocateLoadBalancerNodePorts"), ""))
  5526  	}
  5527  
  5528  	// validate LoadBalancerClass field
  5529  	allErrs = append(allErrs, validateLoadBalancerClassField(nil, service)...)
  5530  
  5531  	// external traffic policy fields
  5532  	allErrs = append(allErrs, validateServiceExternalTrafficPolicy(service)...)
  5533  
  5534  	// internal traffic policy field
  5535  	allErrs = append(allErrs, validateServiceInternalTrafficFieldsValue(service)...)
  5536  
  5537  	return allErrs
  5538  }
  5539  
  5540  func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService bool, allNames *sets.Set[string], fldPath *field.Path) field.ErrorList {
  5541  	allErrs := field.ErrorList{}
  5542  
  5543  	if requireName && len(sp.Name) == 0 {
  5544  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  5545  	} else if len(sp.Name) != 0 {
  5546  		allErrs = append(allErrs, ValidateDNS1123Label(sp.Name, fldPath.Child("name"))...)
  5547  		if allNames.Has(sp.Name) {
  5548  			allErrs = append(allErrs, field.Duplicate(fldPath.Child("name"), sp.Name))
  5549  		} else {
  5550  			allNames.Insert(sp.Name)
  5551  		}
  5552  	}
  5553  
  5554  	for _, msg := range validation.IsValidPortNum(int(sp.Port)) {
  5555  		allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), sp.Port, msg))
  5556  	}
  5557  
  5558  	if len(sp.Protocol) == 0 {
  5559  		allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
  5560  	} else if !supportedPortProtocols.Has(sp.Protocol) {
  5561  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), sp.Protocol, sets.List(supportedPortProtocols)))
  5562  	}
  5563  
  5564  	allErrs = append(allErrs, ValidatePortNumOrName(sp.TargetPort, fldPath.Child("targetPort"))...)
  5565  
  5566  	if sp.AppProtocol != nil {
  5567  		allErrs = append(allErrs, ValidateQualifiedName(*sp.AppProtocol, fldPath.Child("appProtocol"))...)
  5568  	}
  5569  
  5570  	// in the v1 API, targetPorts on headless services were tolerated.
  5571  	// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
  5572  	//
  5573  	// if isHeadlessService {
  5574  	// 	if sp.TargetPort.Type == intstr.String || (sp.TargetPort.Type == intstr.Int && sp.Port != sp.TargetPort.IntValue()) {
  5575  	// 		allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, "must be equal to the value of 'port' when clusterIP = None"))
  5576  	// 	}
  5577  	// }
  5578  
  5579  	return allErrs
  5580  }
  5581  
  5582  var validExternalTrafficPolicies = sets.New(core.ServiceExternalTrafficPolicyCluster, core.ServiceExternalTrafficPolicyLocal)
  5583  
  5584  func validateServiceExternalTrafficPolicy(service *core.Service) field.ErrorList {
  5585  	allErrs := field.ErrorList{}
  5586  
  5587  	fldPath := field.NewPath("spec")
  5588  
  5589  	if !apiservice.ExternallyAccessible(service) {
  5590  		if service.Spec.ExternalTrafficPolicy != "" {
  5591  			allErrs = append(allErrs, field.Invalid(fldPath.Child("externalTrafficPolicy"), service.Spec.ExternalTrafficPolicy,
  5592  				"may only be set for externally-accessible services"))
  5593  		}
  5594  	} else {
  5595  		if service.Spec.ExternalTrafficPolicy == "" {
  5596  			allErrs = append(allErrs, field.Required(fldPath.Child("externalTrafficPolicy"), ""))
  5597  		} else if !validExternalTrafficPolicies.Has(service.Spec.ExternalTrafficPolicy) {
  5598  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("externalTrafficPolicy"),
  5599  				service.Spec.ExternalTrafficPolicy, sets.List(validExternalTrafficPolicies)))
  5600  		}
  5601  	}
  5602  
  5603  	if !apiservice.NeedsHealthCheck(service) {
  5604  		if service.Spec.HealthCheckNodePort != 0 {
  5605  			allErrs = append(allErrs, field.Invalid(fldPath.Child("healthCheckNodePort"), service.Spec.HealthCheckNodePort,
  5606  				"may only be set when `type` is 'LoadBalancer' and `externalTrafficPolicy` is 'Local'"))
  5607  		}
  5608  	} else {
  5609  		if service.Spec.HealthCheckNodePort == 0 {
  5610  			allErrs = append(allErrs, field.Required(fldPath.Child("healthCheckNodePort"), ""))
  5611  		} else {
  5612  			for _, msg := range validation.IsValidPortNum(int(service.Spec.HealthCheckNodePort)) {
  5613  				allErrs = append(allErrs, field.Invalid(fldPath.Child("healthCheckNodePort"), service.Spec.HealthCheckNodePort, msg))
  5614  			}
  5615  		}
  5616  	}
  5617  
  5618  	return allErrs
  5619  }
  5620  
  5621  func validateServiceExternalTrafficFieldsUpdate(before, after *core.Service) field.ErrorList {
  5622  	allErrs := field.ErrorList{}
  5623  
  5624  	if apiservice.NeedsHealthCheck(before) && apiservice.NeedsHealthCheck(after) {
  5625  		if after.Spec.HealthCheckNodePort != before.Spec.HealthCheckNodePort {
  5626  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "healthCheckNodePort"), "field is immutable"))
  5627  		}
  5628  	}
  5629  
  5630  	return allErrs
  5631  }
  5632  
  5633  // validateServiceInternalTrafficFieldsValue validates InternalTraffic related
  5634  // spec have legal value.
  5635  func validateServiceInternalTrafficFieldsValue(service *core.Service) field.ErrorList {
  5636  	allErrs := field.ErrorList{}
  5637  
  5638  	if service.Spec.InternalTrafficPolicy == nil {
  5639  		// We do not forbid internalTrafficPolicy on other Service types because of historical reasons.
  5640  		// We did not check that before it went beta and we don't want to invalidate existing stored objects.
  5641  		if service.Spec.Type == core.ServiceTypeNodePort ||
  5642  			service.Spec.Type == core.ServiceTypeLoadBalancer || service.Spec.Type == core.ServiceTypeClusterIP {
  5643  			allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("internalTrafficPolicy"), ""))
  5644  		}
  5645  	}
  5646  
  5647  	if service.Spec.InternalTrafficPolicy != nil && !supportedServiceInternalTrafficPolicy.Has(*service.Spec.InternalTrafficPolicy) {
  5648  		allErrs = append(allErrs, field.NotSupported(field.NewPath("spec").Child("internalTrafficPolicy"), *service.Spec.InternalTrafficPolicy, sets.List(supportedServiceInternalTrafficPolicy)))
  5649  	}
  5650  
  5651  	return allErrs
  5652  }
  5653  
  5654  // ValidateServiceCreate validates Services as they are created.
  5655  func ValidateServiceCreate(service *core.Service) field.ErrorList {
  5656  	return ValidateService(service)
  5657  }
  5658  
  5659  // ValidateServiceUpdate tests if required fields in the service are set during an update
  5660  func ValidateServiceUpdate(service, oldService *core.Service) field.ErrorList {
  5661  	allErrs := ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta, field.NewPath("metadata"))
  5662  
  5663  	// User can upgrade (add another clusterIP or ipFamily)
  5664  	//      can downgrade (remove secondary clusterIP or ipFamily)
  5665  	// but *CAN NOT* change primary/secondary clusterIP || ipFamily *UNLESS*
  5666  	// they are changing from/to/ON ExternalName
  5667  
  5668  	upgradeDowngradeClusterIPsErrs := validateUpgradeDowngradeClusterIPs(oldService, service)
  5669  	allErrs = append(allErrs, upgradeDowngradeClusterIPsErrs...)
  5670  
  5671  	upgradeDowngradeIPFamiliesErrs := validateUpgradeDowngradeIPFamilies(oldService, service)
  5672  	allErrs = append(allErrs, upgradeDowngradeIPFamiliesErrs...)
  5673  
  5674  	upgradeDowngradeLoadBalancerClassErrs := validateLoadBalancerClassField(oldService, service)
  5675  	allErrs = append(allErrs, upgradeDowngradeLoadBalancerClassErrs...)
  5676  
  5677  	allErrs = append(allErrs, validateServiceExternalTrafficFieldsUpdate(oldService, service)...)
  5678  
  5679  	return append(allErrs, ValidateService(service)...)
  5680  }
  5681  
  5682  // ValidateServiceStatusUpdate tests if required fields in the Service are set when updating status.
  5683  func ValidateServiceStatusUpdate(service, oldService *core.Service) field.ErrorList {
  5684  	allErrs := ValidateObjectMetaUpdate(&service.ObjectMeta, &oldService.ObjectMeta, field.NewPath("metadata"))
  5685  	allErrs = append(allErrs, ValidateLoadBalancerStatus(&service.Status.LoadBalancer, field.NewPath("status", "loadBalancer"), &service.Spec)...)
  5686  	return allErrs
  5687  }
  5688  
  5689  // ValidateReplicationController tests if required fields in the replication controller are set.
  5690  func ValidateReplicationController(controller *core.ReplicationController, opts PodValidationOptions) field.ErrorList {
  5691  	allErrs := ValidateObjectMeta(&controller.ObjectMeta, true, ValidateReplicationControllerName, field.NewPath("metadata"))
  5692  	allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, nil, field.NewPath("spec"), opts)...)
  5693  	return allErrs
  5694  }
  5695  
  5696  // ValidateReplicationControllerUpdate tests if required fields in the replication controller are set.
  5697  func ValidateReplicationControllerUpdate(controller, oldController *core.ReplicationController, opts PodValidationOptions) field.ErrorList {
  5698  	allErrs := ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, field.NewPath("metadata"))
  5699  	allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, &oldController.Spec, field.NewPath("spec"), opts)...)
  5700  	return allErrs
  5701  }
  5702  
  5703  // ValidateReplicationControllerStatusUpdate tests if required fields in the replication controller are set.
  5704  func ValidateReplicationControllerStatusUpdate(controller, oldController *core.ReplicationController) field.ErrorList {
  5705  	allErrs := ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta, field.NewPath("metadata"))
  5706  	allErrs = append(allErrs, ValidateReplicationControllerStatus(controller.Status, field.NewPath("status"))...)
  5707  	return allErrs
  5708  }
  5709  
  5710  func ValidateReplicationControllerStatus(status core.ReplicationControllerStatus, statusPath *field.Path) field.ErrorList {
  5711  	allErrs := field.ErrorList{}
  5712  	allErrs = append(allErrs, ValidateNonnegativeField(int64(status.Replicas), statusPath.Child("replicas"))...)
  5713  	allErrs = append(allErrs, ValidateNonnegativeField(int64(status.FullyLabeledReplicas), statusPath.Child("fullyLabeledReplicas"))...)
  5714  	allErrs = append(allErrs, ValidateNonnegativeField(int64(status.ReadyReplicas), statusPath.Child("readyReplicas"))...)
  5715  	allErrs = append(allErrs, ValidateNonnegativeField(int64(status.AvailableReplicas), statusPath.Child("availableReplicas"))...)
  5716  	allErrs = append(allErrs, ValidateNonnegativeField(int64(status.ObservedGeneration), statusPath.Child("observedGeneration"))...)
  5717  	msg := "cannot be greater than status.replicas"
  5718  	if status.FullyLabeledReplicas > status.Replicas {
  5719  		allErrs = append(allErrs, field.Invalid(statusPath.Child("fullyLabeledReplicas"), status.FullyLabeledReplicas, msg))
  5720  	}
  5721  	if status.ReadyReplicas > status.Replicas {
  5722  		allErrs = append(allErrs, field.Invalid(statusPath.Child("readyReplicas"), status.ReadyReplicas, msg))
  5723  	}
  5724  	if status.AvailableReplicas > status.Replicas {
  5725  		allErrs = append(allErrs, field.Invalid(statusPath.Child("availableReplicas"), status.AvailableReplicas, msg))
  5726  	}
  5727  	if status.AvailableReplicas > status.ReadyReplicas {
  5728  		allErrs = append(allErrs, field.Invalid(statusPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
  5729  	}
  5730  	return allErrs
  5731  }
  5732  
  5733  // Validates that the given selector is non-empty.
  5734  func ValidateNonEmptySelector(selectorMap map[string]string, fldPath *field.Path) field.ErrorList {
  5735  	allErrs := field.ErrorList{}
  5736  	selector := labels.Set(selectorMap).AsSelector()
  5737  	if selector.Empty() {
  5738  		allErrs = append(allErrs, field.Required(fldPath, ""))
  5739  	}
  5740  	return allErrs
  5741  }
  5742  
  5743  // Validates the given template and ensures that it is in accordance with the desired selector and replicas.
  5744  func ValidatePodTemplateSpecForRC(template, oldTemplate *core.PodTemplateSpec, selectorMap map[string]string, replicas int32, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  5745  	allErrs := field.ErrorList{}
  5746  	if template == nil {
  5747  		allErrs = append(allErrs, field.Required(fldPath, ""))
  5748  	} else {
  5749  		selector := labels.Set(selectorMap).AsSelector()
  5750  		if !selector.Empty() {
  5751  			// Verify that the RC selector matches the labels in template.
  5752  			labels := labels.Set(template.Labels)
  5753  			if !selector.Matches(labels) {
  5754  				allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
  5755  			}
  5756  		}
  5757  		allErrs = append(allErrs, ValidatePodTemplateSpec(template, fldPath, opts)...)
  5758  		// get rid of apivalidation.ValidateReadOnlyPersistentDisks,stop passing oldTemplate to this function
  5759  		var oldVols []core.Volume
  5760  		if oldTemplate != nil {
  5761  			oldVols = oldTemplate.Spec.Volumes // +k8s:verify-mutation:reason=clone
  5762  		}
  5763  		if replicas > 1 {
  5764  			allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(template.Spec.Volumes, oldVols, fldPath.Child("spec", "volumes"))...)
  5765  		}
  5766  		// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
  5767  		if template.Spec.RestartPolicy != core.RestartPolicyAlways {
  5768  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []core.RestartPolicy{core.RestartPolicyAlways}))
  5769  		}
  5770  		if template.Spec.ActiveDeadlineSeconds != nil {
  5771  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in ReplicationController is not Supported"))
  5772  		}
  5773  	}
  5774  	return allErrs
  5775  }
  5776  
  5777  // ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
  5778  func ValidateReplicationControllerSpec(spec, oldSpec *core.ReplicationControllerSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  5779  	allErrs := field.ErrorList{}
  5780  	allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
  5781  	allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
  5782  	allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
  5783  	// oldSpec is not empty, pass oldSpec.template.
  5784  	var oldTemplate *core.PodTemplateSpec
  5785  	if oldSpec != nil {
  5786  		oldTemplate = oldSpec.Template // +k8s:verify-mutation:reason=clone
  5787  	}
  5788  	allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, oldTemplate, spec.Selector, spec.Replicas, fldPath.Child("template"), opts)...)
  5789  	return allErrs
  5790  }
  5791  
  5792  // ValidatePodTemplateSpec validates the spec of a pod template
  5793  func ValidatePodTemplateSpec(spec *core.PodTemplateSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  5794  	allErrs := field.ErrorList{}
  5795  	allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.Labels, fldPath.Child("labels"))...)
  5796  	allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
  5797  	allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"), opts)...)
  5798  	allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, nil, fldPath.Child("spec"), opts)...)
  5799  	allErrs = append(allErrs, validateSeccompAnnotationsAndFields(spec.ObjectMeta, &spec.Spec, fldPath.Child("spec"))...)
  5800  
  5801  	if len(spec.Spec.EphemeralContainers) > 0 {
  5802  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "ephemeralContainers"), "ephemeral containers not allowed in pod template"))
  5803  	}
  5804  
  5805  	return allErrs
  5806  }
  5807  
  5808  // ValidateReadOnlyPersistentDisks stick this AFTER the short-circuit checks
  5809  func ValidateReadOnlyPersistentDisks(volumes, oldVolumes []core.Volume, fldPath *field.Path) field.ErrorList {
  5810  	allErrs := field.ErrorList{}
  5811  
  5812  	if utilfeature.DefaultFeatureGate.Enabled(features.SkipReadOnlyValidationGCE) {
  5813  		return field.ErrorList{}
  5814  	}
  5815  
  5816  	isWriteablePD := func(vol *core.Volume) bool {
  5817  		return vol.GCEPersistentDisk != nil && !vol.GCEPersistentDisk.ReadOnly
  5818  	}
  5819  
  5820  	for i := range oldVolumes {
  5821  		if isWriteablePD(&oldVolumes[i]) {
  5822  			return field.ErrorList{}
  5823  		}
  5824  	}
  5825  
  5826  	for i := range volumes {
  5827  		idxPath := fldPath.Index(i)
  5828  		if isWriteablePD(&volumes[i]) {
  5829  			allErrs = append(allErrs, field.Invalid(idxPath.Child("gcePersistentDisk", "readOnly"), false, "must be true for replicated pods > 1; GCE PD can only be mounted on multiple machines if it is read-only"))
  5830  		}
  5831  	}
  5832  	return allErrs
  5833  }
  5834  
  5835  // ValidateTaintsInNodeAnnotations tests that the serialized taints in Node.Annotations has valid data
  5836  func ValidateTaintsInNodeAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  5837  	allErrs := field.ErrorList{}
  5838  
  5839  	taints, err := helper.GetTaintsFromNodeAnnotations(annotations)
  5840  	if err != nil {
  5841  		allErrs = append(allErrs, field.Invalid(fldPath, core.TaintsAnnotationKey, err.Error()))
  5842  		return allErrs
  5843  	}
  5844  
  5845  	if len(taints) > 0 {
  5846  		allErrs = append(allErrs, validateNodeTaints(taints, fldPath.Child(core.TaintsAnnotationKey))...)
  5847  	}
  5848  
  5849  	return allErrs
  5850  }
  5851  
  5852  // validateNodeTaints tests if given taints have valid data.
  5853  func validateNodeTaints(taints []core.Taint, fldPath *field.Path) field.ErrorList {
  5854  	allErrors := field.ErrorList{}
  5855  
  5856  	uniqueTaints := map[core.TaintEffect]sets.Set[string]{}
  5857  
  5858  	for i, currTaint := range taints {
  5859  		idxPath := fldPath.Index(i)
  5860  		// validate the taint key
  5861  		allErrors = append(allErrors, unversionedvalidation.ValidateLabelName(currTaint.Key, idxPath.Child("key"))...)
  5862  		// validate the taint value
  5863  		if errs := validation.IsValidLabelValue(currTaint.Value); len(errs) != 0 {
  5864  			allErrors = append(allErrors, field.Invalid(idxPath.Child("value"), currTaint.Value, strings.Join(errs, ";")))
  5865  		}
  5866  		// validate the taint effect
  5867  		allErrors = append(allErrors, validateTaintEffect(&currTaint.Effect, false, idxPath.Child("effect"))...)
  5868  
  5869  		// validate if taint is unique by <key, effect>
  5870  		if len(uniqueTaints[currTaint.Effect]) > 0 && uniqueTaints[currTaint.Effect].Has(currTaint.Key) {
  5871  			duplicatedError := field.Duplicate(idxPath, currTaint)
  5872  			duplicatedError.Detail = "taints must be unique by key and effect pair"
  5873  			allErrors = append(allErrors, duplicatedError)
  5874  			continue
  5875  		}
  5876  
  5877  		// add taint to existingTaints for uniqueness check
  5878  		if len(uniqueTaints[currTaint.Effect]) == 0 {
  5879  			uniqueTaints[currTaint.Effect] = sets.Set[string]{}
  5880  		}
  5881  		uniqueTaints[currTaint.Effect].Insert(currTaint.Key)
  5882  	}
  5883  	return allErrors
  5884  }
  5885  
  5886  func ValidateNodeSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  5887  	allErrs := field.ErrorList{}
  5888  
  5889  	if annotations[core.TaintsAnnotationKey] != "" {
  5890  		allErrs = append(allErrs, ValidateTaintsInNodeAnnotations(annotations, fldPath)...)
  5891  	}
  5892  
  5893  	if annotations[core.PreferAvoidPodsAnnotationKey] != "" {
  5894  		allErrs = append(allErrs, ValidateAvoidPodsInNodeAnnotations(annotations, fldPath)...)
  5895  	}
  5896  	return allErrs
  5897  }
  5898  
  5899  // ValidateNode tests if required fields in the node are set.
  5900  func ValidateNode(node *core.Node) field.ErrorList {
  5901  	fldPath := field.NewPath("metadata")
  5902  	allErrs := ValidateObjectMeta(&node.ObjectMeta, false, ValidateNodeName, fldPath)
  5903  	allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
  5904  	if len(node.Spec.Taints) > 0 {
  5905  		allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...)
  5906  	}
  5907  
  5908  	// Only validate spec.
  5909  	// All status fields are optional and can be updated later.
  5910  	// That said, if specified, we need to ensure they are valid.
  5911  	allErrs = append(allErrs, ValidateNodeResources(node)...)
  5912  
  5913  	// validate PodCIDRS only if we need to
  5914  	if len(node.Spec.PodCIDRs) > 0 {
  5915  		podCIDRsField := field.NewPath("spec", "podCIDRs")
  5916  
  5917  		// all PodCIDRs should be valid ones
  5918  		for idx, value := range node.Spec.PodCIDRs {
  5919  			if _, err := ValidateCIDR(value); err != nil {
  5920  				allErrs = append(allErrs, field.Invalid(podCIDRsField.Index(idx), node.Spec.PodCIDRs, "must be valid CIDR"))
  5921  			}
  5922  		}
  5923  
  5924  		// if more than PodCIDR then
  5925  		// - validate for dual stack
  5926  		// - validate for duplication
  5927  		if len(node.Spec.PodCIDRs) > 1 {
  5928  			dualStack, err := netutils.IsDualStackCIDRStrings(node.Spec.PodCIDRs)
  5929  			if err != nil {
  5930  				allErrs = append(allErrs, field.InternalError(podCIDRsField, fmt.Errorf("invalid PodCIDRs. failed to check with dual stack with error:%v", err)))
  5931  			}
  5932  			if !dualStack || len(node.Spec.PodCIDRs) > 2 {
  5933  				allErrs = append(allErrs, field.Invalid(podCIDRsField, node.Spec.PodCIDRs, "may specify no more than one CIDR for each IP family"))
  5934  			}
  5935  
  5936  			// PodCIDRs must not contain duplicates
  5937  			seen := sets.Set[string]{}
  5938  			for i, value := range node.Spec.PodCIDRs {
  5939  				if seen.Has(value) {
  5940  					allErrs = append(allErrs, field.Duplicate(podCIDRsField.Index(i), value))
  5941  				}
  5942  				seen.Insert(value)
  5943  			}
  5944  		}
  5945  	}
  5946  
  5947  	return allErrs
  5948  }
  5949  
  5950  // ValidateNodeResources is used to make sure a node has valid capacity and allocatable values.
  5951  func ValidateNodeResources(node *core.Node) field.ErrorList {
  5952  	allErrs := field.ErrorList{}
  5953  
  5954  	// Validate resource quantities in capacity.
  5955  	for k, v := range node.Status.Capacity {
  5956  		resPath := field.NewPath("status", "capacity", string(k))
  5957  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  5958  	}
  5959  
  5960  	// Validate resource quantities in allocatable.
  5961  	for k, v := range node.Status.Allocatable {
  5962  		resPath := field.NewPath("status", "allocatable", string(k))
  5963  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  5964  	}
  5965  	return allErrs
  5966  }
  5967  
  5968  // ValidateNodeUpdate tests to make sure a node update can be applied.  Modifies oldNode.
  5969  func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
  5970  	fldPath := field.NewPath("metadata")
  5971  	allErrs := ValidateObjectMetaUpdate(&node.ObjectMeta, &oldNode.ObjectMeta, fldPath)
  5972  	allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
  5973  
  5974  	// TODO: Enable the code once we have better core object.status update model. Currently,
  5975  	// anyone can update node status.
  5976  	// if !apiequality.Semantic.DeepEqual(node.Status, core.NodeStatus{}) {
  5977  	// 	allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty"))
  5978  	// }
  5979  
  5980  	allErrs = append(allErrs, ValidateNodeResources(node)...)
  5981  
  5982  	// Validate no duplicate addresses in node status.
  5983  	addresses := make(map[core.NodeAddress]bool)
  5984  	for i, address := range node.Status.Addresses {
  5985  		if _, ok := addresses[address]; ok {
  5986  			allErrs = append(allErrs, field.Duplicate(field.NewPath("status", "addresses").Index(i), address))
  5987  		}
  5988  		addresses[address] = true
  5989  	}
  5990  
  5991  	// Allow the controller manager to assign a CIDR to a node if it doesn't have one.
  5992  	if len(oldNode.Spec.PodCIDRs) > 0 {
  5993  		// compare the entire slice
  5994  		if len(oldNode.Spec.PodCIDRs) != len(node.Spec.PodCIDRs) {
  5995  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid"))
  5996  		} else {
  5997  			for idx, value := range oldNode.Spec.PodCIDRs {
  5998  				if value != node.Spec.PodCIDRs[idx] {
  5999  					allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid"))
  6000  				}
  6001  			}
  6002  		}
  6003  	}
  6004  
  6005  	// Allow controller manager updating provider ID when not set
  6006  	if len(oldNode.Spec.ProviderID) > 0 && oldNode.Spec.ProviderID != node.Spec.ProviderID {
  6007  		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "providerID"), "node updates may not change providerID except from \"\" to valid"))
  6008  	}
  6009  
  6010  	if node.Spec.ConfigSource != nil {
  6011  		allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
  6012  	}
  6013  	if node.Status.Config != nil {
  6014  		allErrs = append(allErrs, validateNodeConfigStatus(node.Status.Config, field.NewPath("status", "config"))...)
  6015  	}
  6016  
  6017  	// update taints
  6018  	if len(node.Spec.Taints) > 0 {
  6019  		allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...)
  6020  	}
  6021  
  6022  	if node.Spec.DoNotUseExternalID != oldNode.Spec.DoNotUseExternalID {
  6023  		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "externalID"), "may not be updated"))
  6024  	}
  6025  
  6026  	// status and metadata are allowed change (barring restrictions above), so separately test spec field.
  6027  	// spec only has a few fields, so check the ones we don't allow changing
  6028  	//  1. PodCIDRs - immutable after first set - checked above
  6029  	//  2. ProviderID - immutable after first set - checked above
  6030  	//  3. Unschedulable - allowed to change
  6031  	//  4. Taints - allowed to change
  6032  	//  5. ConfigSource - allowed to change (and checked above)
  6033  	//  6. DoNotUseExternalID - immutable - checked above
  6034  
  6035  	return allErrs
  6036  }
  6037  
  6038  // validation specific to Node.Spec.ConfigSource
  6039  // The field ConfigSource is deprecated and will not be used. The validation is kept in place
  6040  // for the backward compatibility
  6041  func validateNodeConfigSourceSpec(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
  6042  	allErrs := field.ErrorList{}
  6043  	count := int(0)
  6044  	if source.ConfigMap != nil {
  6045  		count++
  6046  		allErrs = append(allErrs, validateConfigMapNodeConfigSourceSpec(source.ConfigMap, fldPath.Child("configMap"))...)
  6047  	}
  6048  	// add more subfields here in the future as they are added to NodeConfigSource
  6049  
  6050  	// exactly one reference subfield must be non-nil
  6051  	if count != 1 {
  6052  		allErrs = append(allErrs, field.Invalid(fldPath, source, "exactly one reference subfield must be non-nil"))
  6053  	}
  6054  	return allErrs
  6055  }
  6056  
  6057  // validation specific to Node.Spec.ConfigSource.ConfigMap
  6058  // The field ConfigSource is deprecated and will not be used. The validation is kept in place
  6059  // for the backward compatibility
  6060  func validateConfigMapNodeConfigSourceSpec(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  6061  	allErrs := field.ErrorList{}
  6062  	// uid and resourceVersion must not be set in spec
  6063  	if string(source.UID) != "" {
  6064  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("uid"), "uid must not be set in spec"))
  6065  	}
  6066  	if source.ResourceVersion != "" {
  6067  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("resourceVersion"), "resourceVersion must not be set in spec"))
  6068  	}
  6069  	return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
  6070  }
  6071  
  6072  // validation specififc to Node.Status.Config
  6073  func validateNodeConfigStatus(status *core.NodeConfigStatus, fldPath *field.Path) field.ErrorList {
  6074  	allErrs := field.ErrorList{}
  6075  	if status.Assigned != nil {
  6076  		allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Assigned, fldPath.Child("assigned"))...)
  6077  	}
  6078  	if status.Active != nil {
  6079  		allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Active, fldPath.Child("active"))...)
  6080  	}
  6081  	if status.LastKnownGood != nil {
  6082  		allErrs = append(allErrs, validateNodeConfigSourceStatus(status.LastKnownGood, fldPath.Child("lastKnownGood"))...)
  6083  	}
  6084  	return allErrs
  6085  }
  6086  
  6087  // validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood)
  6088  func validateNodeConfigSourceStatus(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
  6089  	allErrs := field.ErrorList{}
  6090  	count := int(0)
  6091  	if source.ConfigMap != nil {
  6092  		count++
  6093  		allErrs = append(allErrs, validateConfigMapNodeConfigSourceStatus(source.ConfigMap, fldPath.Child("configMap"))...)
  6094  	}
  6095  	// add more subfields here in the future as they are added to NodeConfigSource
  6096  
  6097  	// exactly one reference subfield must be non-nil
  6098  	if count != 1 {
  6099  		allErrs = append(allErrs, field.Invalid(fldPath, source, "exactly one reference subfield must be non-nil"))
  6100  	}
  6101  	return allErrs
  6102  }
  6103  
  6104  // validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood).ConfigMap
  6105  func validateConfigMapNodeConfigSourceStatus(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  6106  	allErrs := field.ErrorList{}
  6107  	// uid and resourceVersion must be set in status
  6108  	if string(source.UID) == "" {
  6109  		allErrs = append(allErrs, field.Required(fldPath.Child("uid"), "uid must be set in status"))
  6110  	}
  6111  	if source.ResourceVersion == "" {
  6112  		allErrs = append(allErrs, field.Required(fldPath.Child("resourceVersion"), "resourceVersion must be set in status"))
  6113  	}
  6114  	return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
  6115  }
  6116  
  6117  // common validation
  6118  func validateConfigMapNodeConfigSource(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  6119  	allErrs := field.ErrorList{}
  6120  	// validate target configmap namespace
  6121  	if source.Namespace == "" {
  6122  		allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "namespace must be set"))
  6123  	} else {
  6124  		for _, msg := range ValidateNameFunc(ValidateNamespaceName)(source.Namespace, false) {
  6125  			allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), source.Namespace, msg))
  6126  		}
  6127  	}
  6128  	// validate target configmap name
  6129  	if source.Name == "" {
  6130  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name must be set"))
  6131  	} else {
  6132  		for _, msg := range ValidateNameFunc(ValidateConfigMapName)(source.Name, false) {
  6133  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), source.Name, msg))
  6134  		}
  6135  	}
  6136  	// validate kubeletConfigKey against rules for configMap key names
  6137  	if source.KubeletConfigKey == "" {
  6138  		allErrs = append(allErrs, field.Required(fldPath.Child("kubeletConfigKey"), "kubeletConfigKey must be set"))
  6139  	} else {
  6140  		for _, msg := range validation.IsConfigMapKey(source.KubeletConfigKey) {
  6141  			allErrs = append(allErrs, field.Invalid(fldPath.Child("kubeletConfigKey"), source.KubeletConfigKey, msg))
  6142  		}
  6143  	}
  6144  	return allErrs
  6145  }
  6146  
  6147  // Validate compute resource typename.
  6148  // Refer to docs/design/resources.md for more details.
  6149  func validateResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6150  	allErrs := field.ErrorList{}
  6151  	for _, msg := range validation.IsQualifiedName(string(value)) {
  6152  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  6153  	}
  6154  	if len(allErrs) != 0 {
  6155  		return allErrs
  6156  	}
  6157  
  6158  	if len(strings.Split(string(value), "/")) == 1 {
  6159  		if !helper.IsStandardResourceName(value) {
  6160  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource type or fully qualified"))
  6161  		}
  6162  	}
  6163  
  6164  	return allErrs
  6165  }
  6166  
  6167  // Validate container resource name
  6168  // Refer to docs/design/resources.md for more details.
  6169  func validateContainerResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6170  	allErrs := validateResourceName(value, fldPath)
  6171  
  6172  	if len(strings.Split(string(value), "/")) == 1 {
  6173  		if !helper.IsStandardContainerResourceName(value) {
  6174  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource for containers"))
  6175  		}
  6176  	} else if !helper.IsNativeResource(value) {
  6177  		if !helper.IsExtendedResourceName(value) {
  6178  			return append(allErrs, field.Invalid(fldPath, value, "doesn't follow extended resource name standard"))
  6179  		}
  6180  	}
  6181  	return allErrs
  6182  }
  6183  
  6184  // Validate resource names that can go in a resource quota
  6185  // Refer to docs/design/resources.md for more details.
  6186  func ValidateResourceQuotaResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6187  	allErrs := validateResourceName(value, fldPath)
  6188  
  6189  	if len(strings.Split(string(value), "/")) == 1 {
  6190  		if !helper.IsStandardQuotaResourceName(value) {
  6191  			return append(allErrs, field.Invalid(fldPath, value, isInvalidQuotaResource))
  6192  		}
  6193  	}
  6194  	return allErrs
  6195  }
  6196  
  6197  // Validate limit range types
  6198  func validateLimitRangeTypeName(value core.LimitType, fldPath *field.Path) field.ErrorList {
  6199  	allErrs := field.ErrorList{}
  6200  	for _, msg := range validation.IsQualifiedName(string(value)) {
  6201  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  6202  	}
  6203  	if len(allErrs) != 0 {
  6204  		return allErrs
  6205  	}
  6206  
  6207  	if len(strings.Split(string(value), "/")) == 1 {
  6208  		if !helper.IsStandardLimitRangeType(value) {
  6209  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard limit type or fully qualified"))
  6210  		}
  6211  	}
  6212  
  6213  	return allErrs
  6214  }
  6215  
  6216  // Validate limit range resource name
  6217  // limit types (other than Pod/Container) could contain storage not just cpu or memory
  6218  func validateLimitRangeResourceName(limitType core.LimitType, value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6219  	switch limitType {
  6220  	case core.LimitTypePod, core.LimitTypeContainer:
  6221  		return validateContainerResourceName(value, fldPath)
  6222  	default:
  6223  		return validateResourceName(value, fldPath)
  6224  	}
  6225  }
  6226  
  6227  // ValidateLimitRange tests if required fields in the LimitRange are set.
  6228  func ValidateLimitRange(limitRange *core.LimitRange) field.ErrorList {
  6229  	allErrs := ValidateObjectMeta(&limitRange.ObjectMeta, true, ValidateLimitRangeName, field.NewPath("metadata"))
  6230  
  6231  	// ensure resource names are properly qualified per docs/design/resources.md
  6232  	limitTypeSet := map[core.LimitType]bool{}
  6233  	fldPath := field.NewPath("spec", "limits")
  6234  	for i := range limitRange.Spec.Limits {
  6235  		idxPath := fldPath.Index(i)
  6236  		limit := &limitRange.Spec.Limits[i]
  6237  		allErrs = append(allErrs, validateLimitRangeTypeName(limit.Type, idxPath.Child("type"))...)
  6238  
  6239  		_, found := limitTypeSet[limit.Type]
  6240  		if found {
  6241  			allErrs = append(allErrs, field.Duplicate(idxPath.Child("type"), limit.Type))
  6242  		}
  6243  		limitTypeSet[limit.Type] = true
  6244  
  6245  		keys := sets.Set[string]{}
  6246  		min := map[string]resource.Quantity{}
  6247  		max := map[string]resource.Quantity{}
  6248  		defaults := map[string]resource.Quantity{}
  6249  		defaultRequests := map[string]resource.Quantity{}
  6250  		maxLimitRequestRatios := map[string]resource.Quantity{}
  6251  
  6252  		for k, q := range limit.Max {
  6253  			allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("max").Key(string(k)))...)
  6254  			keys.Insert(string(k))
  6255  			max[string(k)] = q
  6256  		}
  6257  		for k, q := range limit.Min {
  6258  			allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("min").Key(string(k)))...)
  6259  			keys.Insert(string(k))
  6260  			min[string(k)] = q
  6261  		}
  6262  
  6263  		if limit.Type == core.LimitTypePod {
  6264  			if len(limit.Default) > 0 {
  6265  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("default"), "may not be specified when `type` is 'Pod'"))
  6266  			}
  6267  			if len(limit.DefaultRequest) > 0 {
  6268  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("defaultRequest"), "may not be specified when `type` is 'Pod'"))
  6269  			}
  6270  		} else {
  6271  			for k, q := range limit.Default {
  6272  				allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("default").Key(string(k)))...)
  6273  				keys.Insert(string(k))
  6274  				defaults[string(k)] = q
  6275  			}
  6276  			for k, q := range limit.DefaultRequest {
  6277  				allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("defaultRequest").Key(string(k)))...)
  6278  				keys.Insert(string(k))
  6279  				defaultRequests[string(k)] = q
  6280  			}
  6281  		}
  6282  
  6283  		if limit.Type == core.LimitTypePersistentVolumeClaim {
  6284  			_, minQuantityFound := limit.Min[core.ResourceStorage]
  6285  			_, maxQuantityFound := limit.Max[core.ResourceStorage]
  6286  			if !minQuantityFound && !maxQuantityFound {
  6287  				allErrs = append(allErrs, field.Required(idxPath.Child("limits"), "either minimum or maximum storage value is required, but neither was provided"))
  6288  			}
  6289  		}
  6290  
  6291  		for k, q := range limit.MaxLimitRequestRatio {
  6292  			allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("maxLimitRequestRatio").Key(string(k)))...)
  6293  			keys.Insert(string(k))
  6294  			maxLimitRequestRatios[string(k)] = q
  6295  		}
  6296  
  6297  		for k := range keys {
  6298  			minQuantity, minQuantityFound := min[k]
  6299  			maxQuantity, maxQuantityFound := max[k]
  6300  			defaultQuantity, defaultQuantityFound := defaults[k]
  6301  			defaultRequestQuantity, defaultRequestQuantityFound := defaultRequests[k]
  6302  			maxRatio, maxRatioFound := maxLimitRequestRatios[k]
  6303  
  6304  			if minQuantityFound && maxQuantityFound && minQuantity.Cmp(maxQuantity) > 0 {
  6305  				allErrs = append(allErrs, field.Invalid(idxPath.Child("min").Key(string(k)), minQuantity, fmt.Sprintf("min value %s is greater than max value %s", minQuantity.String(), maxQuantity.String())))
  6306  			}
  6307  
  6308  			if defaultRequestQuantityFound && minQuantityFound && minQuantity.Cmp(defaultRequestQuantity) > 0 {
  6309  				allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("min value %s is greater than default request value %s", minQuantity.String(), defaultRequestQuantity.String())))
  6310  			}
  6311  
  6312  			if defaultRequestQuantityFound && maxQuantityFound && defaultRequestQuantity.Cmp(maxQuantity) > 0 {
  6313  				allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("default request value %s is greater than max value %s", defaultRequestQuantity.String(), maxQuantity.String())))
  6314  			}
  6315  
  6316  			if defaultRequestQuantityFound && defaultQuantityFound && defaultRequestQuantity.Cmp(defaultQuantity) > 0 {
  6317  				allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("default request value %s is greater than default limit value %s", defaultRequestQuantity.String(), defaultQuantity.String())))
  6318  			}
  6319  
  6320  			if defaultQuantityFound && minQuantityFound && minQuantity.Cmp(defaultQuantity) > 0 {
  6321  				allErrs = append(allErrs, field.Invalid(idxPath.Child("default").Key(string(k)), minQuantity, fmt.Sprintf("min value %s is greater than default value %s", minQuantity.String(), defaultQuantity.String())))
  6322  			}
  6323  
  6324  			if defaultQuantityFound && maxQuantityFound && defaultQuantity.Cmp(maxQuantity) > 0 {
  6325  				allErrs = append(allErrs, field.Invalid(idxPath.Child("default").Key(string(k)), maxQuantity, fmt.Sprintf("default value %s is greater than max value %s", defaultQuantity.String(), maxQuantity.String())))
  6326  			}
  6327  			if maxRatioFound && maxRatio.Cmp(*resource.NewQuantity(1, resource.DecimalSI)) < 0 {
  6328  				allErrs = append(allErrs, field.Invalid(idxPath.Child("maxLimitRequestRatio").Key(string(k)), maxRatio, fmt.Sprintf("ratio %s is less than 1", maxRatio.String())))
  6329  			}
  6330  			if maxRatioFound && minQuantityFound && maxQuantityFound {
  6331  				maxRatioValue := float64(maxRatio.Value())
  6332  				minQuantityValue := minQuantity.Value()
  6333  				maxQuantityValue := maxQuantity.Value()
  6334  				if maxRatio.Value() < resource.MaxMilliValue && minQuantityValue < resource.MaxMilliValue && maxQuantityValue < resource.MaxMilliValue {
  6335  					maxRatioValue = float64(maxRatio.MilliValue()) / 1000
  6336  					minQuantityValue = minQuantity.MilliValue()
  6337  					maxQuantityValue = maxQuantity.MilliValue()
  6338  				}
  6339  				maxRatioLimit := float64(maxQuantityValue) / float64(minQuantityValue)
  6340  				if maxRatioValue > maxRatioLimit {
  6341  					allErrs = append(allErrs, field.Invalid(idxPath.Child("maxLimitRequestRatio").Key(string(k)), maxRatio, fmt.Sprintf("ratio %s is greater than max/min = %f", maxRatio.String(), maxRatioLimit)))
  6342  				}
  6343  			}
  6344  
  6345  			// for GPU, hugepages and other resources that are not allowed to overcommit,
  6346  			// the default value and defaultRequest value must match if both are specified
  6347  			if !helper.IsOvercommitAllowed(core.ResourceName(k)) && defaultQuantityFound && defaultRequestQuantityFound && defaultQuantity.Cmp(defaultRequestQuantity) != 0 {
  6348  				allErrs = append(allErrs, field.Invalid(idxPath.Child("defaultRequest").Key(string(k)), defaultRequestQuantity, fmt.Sprintf("default value %s must equal to defaultRequest value %s in %s", defaultQuantity.String(), defaultRequestQuantity.String(), k)))
  6349  			}
  6350  		}
  6351  	}
  6352  
  6353  	return allErrs
  6354  }
  6355  
  6356  // ValidateServiceAccount tests if required fields in the ServiceAccount are set.
  6357  func ValidateServiceAccount(serviceAccount *core.ServiceAccount) field.ErrorList {
  6358  	allErrs := ValidateObjectMeta(&serviceAccount.ObjectMeta, true, ValidateServiceAccountName, field.NewPath("metadata"))
  6359  	return allErrs
  6360  }
  6361  
  6362  // ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set.
  6363  func ValidateServiceAccountUpdate(newServiceAccount, oldServiceAccount *core.ServiceAccount) field.ErrorList {
  6364  	allErrs := ValidateObjectMetaUpdate(&newServiceAccount.ObjectMeta, &oldServiceAccount.ObjectMeta, field.NewPath("metadata"))
  6365  	allErrs = append(allErrs, ValidateServiceAccount(newServiceAccount)...)
  6366  	return allErrs
  6367  }
  6368  
  6369  // ValidateSecret tests if required fields in the Secret are set.
  6370  func ValidateSecret(secret *core.Secret) field.ErrorList {
  6371  	allErrs := ValidateObjectMeta(&secret.ObjectMeta, true, ValidateSecretName, field.NewPath("metadata"))
  6372  
  6373  	dataPath := field.NewPath("data")
  6374  	totalSize := 0
  6375  	for key, value := range secret.Data {
  6376  		for _, msg := range validation.IsConfigMapKey(key) {
  6377  			allErrs = append(allErrs, field.Invalid(dataPath.Key(key), key, msg))
  6378  		}
  6379  		totalSize += len(value)
  6380  	}
  6381  	if totalSize > core.MaxSecretSize {
  6382  		allErrs = append(allErrs, field.TooLong(dataPath, "", core.MaxSecretSize))
  6383  	}
  6384  
  6385  	switch secret.Type {
  6386  	case core.SecretTypeServiceAccountToken:
  6387  		// Only require Annotations[kubernetes.io/service-account.name]
  6388  		// Additional fields (like Annotations[kubernetes.io/service-account.uid] and Data[token]) might be contributed later by a controller loop
  6389  		if value := secret.Annotations[core.ServiceAccountNameKey]; len(value) == 0 {
  6390  			allErrs = append(allErrs, field.Required(field.NewPath("metadata", "annotations").Key(core.ServiceAccountNameKey), ""))
  6391  		}
  6392  	case core.SecretTypeOpaque, "":
  6393  	// no-op
  6394  	case core.SecretTypeDockercfg:
  6395  		dockercfgBytes, exists := secret.Data[core.DockerConfigKey]
  6396  		if !exists {
  6397  			allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigKey), ""))
  6398  			break
  6399  		}
  6400  
  6401  		// make sure that the content is well-formed json.
  6402  		if err := json.Unmarshal(dockercfgBytes, &map[string]interface{}{}); err != nil {
  6403  			allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigKey), "<secret contents redacted>", err.Error()))
  6404  		}
  6405  	case core.SecretTypeDockerConfigJSON:
  6406  		dockerConfigJSONBytes, exists := secret.Data[core.DockerConfigJSONKey]
  6407  		if !exists {
  6408  			allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigJSONKey), ""))
  6409  			break
  6410  		}
  6411  
  6412  		// make sure that the content is well-formed json.
  6413  		if err := json.Unmarshal(dockerConfigJSONBytes, &map[string]interface{}{}); err != nil {
  6414  			allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigJSONKey), "<secret contents redacted>", err.Error()))
  6415  		}
  6416  	case core.SecretTypeBasicAuth:
  6417  		_, usernameFieldExists := secret.Data[core.BasicAuthUsernameKey]
  6418  		_, passwordFieldExists := secret.Data[core.BasicAuthPasswordKey]
  6419  
  6420  		// username or password might be empty, but the field must be present
  6421  		if !usernameFieldExists && !passwordFieldExists {
  6422  			allErrs = append(allErrs, field.Required(dataPath.Key(core.BasicAuthUsernameKey), ""))
  6423  			allErrs = append(allErrs, field.Required(dataPath.Key(core.BasicAuthPasswordKey), ""))
  6424  			break
  6425  		}
  6426  	case core.SecretTypeSSHAuth:
  6427  		if len(secret.Data[core.SSHAuthPrivateKey]) == 0 {
  6428  			allErrs = append(allErrs, field.Required(dataPath.Key(core.SSHAuthPrivateKey), ""))
  6429  			break
  6430  		}
  6431  
  6432  	case core.SecretTypeTLS:
  6433  		if _, exists := secret.Data[core.TLSCertKey]; !exists {
  6434  			allErrs = append(allErrs, field.Required(dataPath.Key(core.TLSCertKey), ""))
  6435  		}
  6436  		if _, exists := secret.Data[core.TLSPrivateKeyKey]; !exists {
  6437  			allErrs = append(allErrs, field.Required(dataPath.Key(core.TLSPrivateKeyKey), ""))
  6438  		}
  6439  	default:
  6440  		// no-op
  6441  	}
  6442  
  6443  	return allErrs
  6444  }
  6445  
  6446  // ValidateSecretUpdate tests if required fields in the Secret are set.
  6447  func ValidateSecretUpdate(newSecret, oldSecret *core.Secret) field.ErrorList {
  6448  	allErrs := ValidateObjectMetaUpdate(&newSecret.ObjectMeta, &oldSecret.ObjectMeta, field.NewPath("metadata"))
  6449  
  6450  	allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, field.NewPath("type"))...)
  6451  	if oldSecret.Immutable != nil && *oldSecret.Immutable {
  6452  		if newSecret.Immutable == nil || !*newSecret.Immutable {
  6453  			allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set"))
  6454  		}
  6455  		if !reflect.DeepEqual(newSecret.Data, oldSecret.Data) {
  6456  			allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set"))
  6457  		}
  6458  		// We don't validate StringData, as it was already converted back to Data
  6459  		// before validation is happening.
  6460  	}
  6461  
  6462  	allErrs = append(allErrs, ValidateSecret(newSecret)...)
  6463  	return allErrs
  6464  }
  6465  
  6466  // ValidateConfigMapName can be used to check whether the given ConfigMap name is valid.
  6467  // Prefix indicates this name will be used as part of generation, in which case
  6468  // trailing dashes are allowed.
  6469  var ValidateConfigMapName = apimachineryvalidation.NameIsDNSSubdomain
  6470  
  6471  // ValidateConfigMap tests whether required fields in the ConfigMap are set.
  6472  func ValidateConfigMap(cfg *core.ConfigMap) field.ErrorList {
  6473  	allErrs := field.ErrorList{}
  6474  	allErrs = append(allErrs, ValidateObjectMeta(&cfg.ObjectMeta, true, ValidateConfigMapName, field.NewPath("metadata"))...)
  6475  
  6476  	totalSize := 0
  6477  
  6478  	for key, value := range cfg.Data {
  6479  		for _, msg := range validation.IsConfigMapKey(key) {
  6480  			allErrs = append(allErrs, field.Invalid(field.NewPath("data").Key(key), key, msg))
  6481  		}
  6482  		// check if we have a duplicate key in the other bag
  6483  		if _, isValue := cfg.BinaryData[key]; isValue {
  6484  			msg := "duplicate of key present in binaryData"
  6485  			allErrs = append(allErrs, field.Invalid(field.NewPath("data").Key(key), key, msg))
  6486  		}
  6487  		totalSize += len(value)
  6488  	}
  6489  	for key, value := range cfg.BinaryData {
  6490  		for _, msg := range validation.IsConfigMapKey(key) {
  6491  			allErrs = append(allErrs, field.Invalid(field.NewPath("binaryData").Key(key), key, msg))
  6492  		}
  6493  		totalSize += len(value)
  6494  	}
  6495  	if totalSize > core.MaxSecretSize {
  6496  		// pass back "" to indicate that the error refers to the whole object.
  6497  		allErrs = append(allErrs, field.TooLong(field.NewPath(""), cfg, core.MaxSecretSize))
  6498  	}
  6499  
  6500  	return allErrs
  6501  }
  6502  
  6503  // ValidateConfigMapUpdate tests if required fields in the ConfigMap are set.
  6504  func ValidateConfigMapUpdate(newCfg, oldCfg *core.ConfigMap) field.ErrorList {
  6505  	allErrs := field.ErrorList{}
  6506  	allErrs = append(allErrs, ValidateObjectMetaUpdate(&newCfg.ObjectMeta, &oldCfg.ObjectMeta, field.NewPath("metadata"))...)
  6507  
  6508  	if oldCfg.Immutable != nil && *oldCfg.Immutable {
  6509  		if newCfg.Immutable == nil || !*newCfg.Immutable {
  6510  			allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set"))
  6511  		}
  6512  		if !reflect.DeepEqual(newCfg.Data, oldCfg.Data) {
  6513  			allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set"))
  6514  		}
  6515  		if !reflect.DeepEqual(newCfg.BinaryData, oldCfg.BinaryData) {
  6516  			allErrs = append(allErrs, field.Forbidden(field.NewPath("binaryData"), "field is immutable when `immutable` is set"))
  6517  		}
  6518  	}
  6519  
  6520  	allErrs = append(allErrs, ValidateConfigMap(newCfg)...)
  6521  	return allErrs
  6522  }
  6523  
  6524  func validateBasicResource(quantity resource.Quantity, fldPath *field.Path) field.ErrorList {
  6525  	if quantity.Value() < 0 {
  6526  		return field.ErrorList{field.Invalid(fldPath, quantity.Value(), "must be a valid resource quantity")}
  6527  	}
  6528  	return field.ErrorList{}
  6529  }
  6530  
  6531  // Validates resource requirement spec.
  6532  func ValidateResourceRequirements(requirements *core.ResourceRequirements, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  6533  	allErrs := field.ErrorList{}
  6534  	limPath := fldPath.Child("limits")
  6535  	reqPath := fldPath.Child("requests")
  6536  	limContainsCPUOrMemory := false
  6537  	reqContainsCPUOrMemory := false
  6538  	limContainsHugePages := false
  6539  	reqContainsHugePages := false
  6540  	supportedQoSComputeResources := sets.New(core.ResourceCPU, core.ResourceMemory)
  6541  	for resourceName, quantity := range requirements.Limits {
  6542  
  6543  		fldPath := limPath.Key(string(resourceName))
  6544  		// Validate resource name.
  6545  		allErrs = append(allErrs, validateContainerResourceName(resourceName, fldPath)...)
  6546  
  6547  		// Validate resource quantity.
  6548  		allErrs = append(allErrs, ValidateResourceQuantityValue(resourceName, quantity, fldPath)...)
  6549  
  6550  		if helper.IsHugePageResourceName(resourceName) {
  6551  			limContainsHugePages = true
  6552  			if err := validateResourceQuantityHugePageValue(resourceName, quantity, opts); err != nil {
  6553  				allErrs = append(allErrs, field.Invalid(fldPath, quantity.String(), err.Error()))
  6554  			}
  6555  		}
  6556  
  6557  		if supportedQoSComputeResources.Has(resourceName) {
  6558  			limContainsCPUOrMemory = true
  6559  		}
  6560  	}
  6561  	for resourceName, quantity := range requirements.Requests {
  6562  		fldPath := reqPath.Key(string(resourceName))
  6563  		// Validate resource name.
  6564  		allErrs = append(allErrs, validateContainerResourceName(resourceName, fldPath)...)
  6565  		// Validate resource quantity.
  6566  		allErrs = append(allErrs, ValidateResourceQuantityValue(resourceName, quantity, fldPath)...)
  6567  
  6568  		// Check that request <= limit.
  6569  		limitQuantity, exists := requirements.Limits[resourceName]
  6570  		if exists {
  6571  			// For non overcommitable resources, not only requests can't exceed limits, they also can't be lower, i.e. must be equal.
  6572  			if quantity.Cmp(limitQuantity) != 0 && !helper.IsOvercommitAllowed(resourceName) {
  6573  				allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be equal to %s limit of %s", resourceName, limitQuantity.String())))
  6574  			} else if quantity.Cmp(limitQuantity) > 0 {
  6575  				allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be less than or equal to %s limit of %s", resourceName, limitQuantity.String())))
  6576  			}
  6577  		} else if !helper.IsOvercommitAllowed(resourceName) {
  6578  			allErrs = append(allErrs, field.Required(limPath, "Limit must be set for non overcommitable resources"))
  6579  		}
  6580  		if helper.IsHugePageResourceName(resourceName) {
  6581  			reqContainsHugePages = true
  6582  			if err := validateResourceQuantityHugePageValue(resourceName, quantity, opts); err != nil {
  6583  				allErrs = append(allErrs, field.Invalid(fldPath, quantity.String(), err.Error()))
  6584  			}
  6585  		}
  6586  		if supportedQoSComputeResources.Has(resourceName) {
  6587  			reqContainsCPUOrMemory = true
  6588  		}
  6589  
  6590  	}
  6591  	if !limContainsCPUOrMemory && !reqContainsCPUOrMemory && (reqContainsHugePages || limContainsHugePages) {
  6592  		allErrs = append(allErrs, field.Forbidden(fldPath, "HugePages require cpu or memory"))
  6593  	}
  6594  
  6595  	allErrs = append(allErrs, validateResourceClaimNames(requirements.Claims, podClaimNames, fldPath.Child("claims"))...)
  6596  
  6597  	return allErrs
  6598  }
  6599  
  6600  // validateResourceClaimNames checks that the names in
  6601  // ResourceRequirements.Claims have a corresponding entry in
  6602  // PodSpec.ResourceClaims.
  6603  func validateResourceClaimNames(claims []core.ResourceClaim, podClaimNames sets.Set[string], fldPath *field.Path) field.ErrorList {
  6604  	var allErrs field.ErrorList
  6605  	names := sets.Set[string]{}
  6606  	for i, claim := range claims {
  6607  		name := claim.Name
  6608  		if name == "" {
  6609  			allErrs = append(allErrs, field.Required(fldPath.Index(i), ""))
  6610  		} else {
  6611  			if names.Has(name) {
  6612  				allErrs = append(allErrs, field.Duplicate(fldPath.Index(i), name))
  6613  			} else {
  6614  				names.Insert(name)
  6615  			}
  6616  			if !podClaimNames.Has(name) {
  6617  				// field.NotFound doesn't accept an
  6618  				// explanation. Adding one here is more
  6619  				// user-friendly.
  6620  				error := field.NotFound(fldPath.Index(i), name)
  6621  				error.Detail = "must be one of the names in pod.spec.resourceClaims"
  6622  				if len(podClaimNames) == 0 {
  6623  					error.Detail += " which is empty"
  6624  				} else {
  6625  					error.Detail += ": " + strings.Join(sets.List(podClaimNames), ", ")
  6626  				}
  6627  				allErrs = append(allErrs, error)
  6628  			}
  6629  		}
  6630  	}
  6631  	return allErrs
  6632  }
  6633  
  6634  func validateResourceQuantityHugePageValue(name core.ResourceName, quantity resource.Quantity, opts PodValidationOptions) error {
  6635  	if !helper.IsHugePageResourceName(name) {
  6636  		return nil
  6637  	}
  6638  
  6639  	if !opts.AllowIndivisibleHugePagesValues && !helper.IsHugePageResourceValueDivisible(name, quantity) {
  6640  		return fmt.Errorf("%s is not positive integer multiple of %s", quantity.String(), name)
  6641  	}
  6642  
  6643  	return nil
  6644  }
  6645  
  6646  // validateResourceQuotaScopes ensures that each enumerated hard resource constraint is valid for set of scopes
  6647  func validateResourceQuotaScopes(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6648  	allErrs := field.ErrorList{}
  6649  	if len(resourceQuotaSpec.Scopes) == 0 {
  6650  		return allErrs
  6651  	}
  6652  	hardLimits := sets.New[core.ResourceName]()
  6653  	for k := range resourceQuotaSpec.Hard {
  6654  		hardLimits.Insert(k)
  6655  	}
  6656  	fldPath := fld.Child("scopes")
  6657  	scopeSet := sets.New[core.ResourceQuotaScope]()
  6658  	for _, scope := range resourceQuotaSpec.Scopes {
  6659  		if !helper.IsStandardResourceQuotaScope(scope) {
  6660  			allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "unsupported scope"))
  6661  		}
  6662  		for _, k := range sets.List(hardLimits) {
  6663  			if helper.IsStandardQuotaResourceName(k) && !helper.IsResourceQuotaScopeValidForResource(scope, k) {
  6664  				allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "unsupported scope applied to resource"))
  6665  			}
  6666  		}
  6667  		scopeSet.Insert(scope)
  6668  	}
  6669  	invalidScopePairs := []sets.Set[core.ResourceQuotaScope]{
  6670  		sets.New(core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort),
  6671  		sets.New(core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating),
  6672  	}
  6673  	for _, invalidScopePair := range invalidScopePairs {
  6674  		if scopeSet.HasAll(sets.List(invalidScopePair)...) {
  6675  			allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "conflicting scopes"))
  6676  		}
  6677  	}
  6678  	return allErrs
  6679  }
  6680  
  6681  // validateScopedResourceSelectorRequirement tests that the match expressions has valid data
  6682  func validateScopedResourceSelectorRequirement(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6683  	allErrs := field.ErrorList{}
  6684  	hardLimits := sets.New[core.ResourceName]()
  6685  	for k := range resourceQuotaSpec.Hard {
  6686  		hardLimits.Insert(k)
  6687  	}
  6688  	fldPath := fld.Child("matchExpressions")
  6689  	scopeSet := sets.New[core.ResourceQuotaScope]()
  6690  	for _, req := range resourceQuotaSpec.ScopeSelector.MatchExpressions {
  6691  		if !helper.IsStandardResourceQuotaScope(req.ScopeName) {
  6692  			allErrs = append(allErrs, field.Invalid(fldPath.Child("scopeName"), req.ScopeName, "unsupported scope"))
  6693  		}
  6694  		for _, k := range sets.List(hardLimits) {
  6695  			if helper.IsStandardQuotaResourceName(k) && !helper.IsResourceQuotaScopeValidForResource(req.ScopeName, k) {
  6696  				allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.ScopeSelector, "unsupported scope applied to resource"))
  6697  			}
  6698  		}
  6699  		switch req.ScopeName {
  6700  		case core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort, core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating, core.ResourceQuotaScopeCrossNamespacePodAffinity:
  6701  			if req.Operator != core.ScopeSelectorOpExists {
  6702  				allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator,
  6703  					"must be 'Exist' when scope is any of ResourceQuotaScopeTerminating, ResourceQuotaScopeNotTerminating, ResourceQuotaScopeBestEffort, ResourceQuotaScopeNotBestEffort or ResourceQuotaScopeCrossNamespacePodAffinity"))
  6704  			}
  6705  		}
  6706  
  6707  		switch req.Operator {
  6708  		case core.ScopeSelectorOpIn, core.ScopeSelectorOpNotIn:
  6709  			if len(req.Values) == 0 {
  6710  				allErrs = append(allErrs, field.Required(fldPath.Child("values"),
  6711  					"must be at least one value when `operator` is 'In' or 'NotIn' for scope selector"))
  6712  			}
  6713  		case core.ScopeSelectorOpExists, core.ScopeSelectorOpDoesNotExist:
  6714  			if len(req.Values) != 0 {
  6715  				allErrs = append(allErrs, field.Invalid(fldPath.Child("values"), req.Values,
  6716  					"must be no value when `operator` is 'Exist' or 'DoesNotExist' for scope selector"))
  6717  			}
  6718  		default:
  6719  			allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator, "not a valid selector operator"))
  6720  		}
  6721  		scopeSet.Insert(req.ScopeName)
  6722  	}
  6723  	invalidScopePairs := []sets.Set[core.ResourceQuotaScope]{
  6724  		sets.New(core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort),
  6725  		sets.New(core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating),
  6726  	}
  6727  	for _, invalidScopePair := range invalidScopePairs {
  6728  		if scopeSet.HasAll(sets.List(invalidScopePair)...) {
  6729  			allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "conflicting scopes"))
  6730  		}
  6731  	}
  6732  
  6733  	return allErrs
  6734  }
  6735  
  6736  // validateScopeSelector tests that the specified scope selector has valid data
  6737  func validateScopeSelector(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6738  	allErrs := field.ErrorList{}
  6739  	if resourceQuotaSpec.ScopeSelector == nil {
  6740  		return allErrs
  6741  	}
  6742  	allErrs = append(allErrs, validateScopedResourceSelectorRequirement(resourceQuotaSpec, fld.Child("scopeSelector"))...)
  6743  	return allErrs
  6744  }
  6745  
  6746  // ValidateResourceQuota tests if required fields in the ResourceQuota are set.
  6747  func ValidateResourceQuota(resourceQuota *core.ResourceQuota) field.ErrorList {
  6748  	allErrs := ValidateObjectMeta(&resourceQuota.ObjectMeta, true, ValidateResourceQuotaName, field.NewPath("metadata"))
  6749  
  6750  	allErrs = append(allErrs, ValidateResourceQuotaSpec(&resourceQuota.Spec, field.NewPath("spec"))...)
  6751  	allErrs = append(allErrs, ValidateResourceQuotaStatus(&resourceQuota.Status, field.NewPath("status"))...)
  6752  
  6753  	return allErrs
  6754  }
  6755  
  6756  func ValidateResourceQuotaStatus(status *core.ResourceQuotaStatus, fld *field.Path) field.ErrorList {
  6757  	allErrs := field.ErrorList{}
  6758  
  6759  	fldPath := fld.Child("hard")
  6760  	for k, v := range status.Hard {
  6761  		resPath := fldPath.Key(string(k))
  6762  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6763  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6764  	}
  6765  	fldPath = fld.Child("used")
  6766  	for k, v := range status.Used {
  6767  		resPath := fldPath.Key(string(k))
  6768  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6769  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6770  	}
  6771  
  6772  	return allErrs
  6773  }
  6774  
  6775  func ValidateResourceQuotaSpec(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6776  	allErrs := field.ErrorList{}
  6777  
  6778  	fldPath := fld.Child("hard")
  6779  	for k, v := range resourceQuotaSpec.Hard {
  6780  		resPath := fldPath.Key(string(k))
  6781  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6782  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6783  	}
  6784  
  6785  	allErrs = append(allErrs, validateResourceQuotaScopes(resourceQuotaSpec, fld)...)
  6786  	allErrs = append(allErrs, validateScopeSelector(resourceQuotaSpec, fld)...)
  6787  
  6788  	return allErrs
  6789  }
  6790  
  6791  // ValidateResourceQuantityValue enforces that specified quantity is valid for specified resource
  6792  func ValidateResourceQuantityValue(resource core.ResourceName, value resource.Quantity, fldPath *field.Path) field.ErrorList {
  6793  	allErrs := field.ErrorList{}
  6794  	allErrs = append(allErrs, ValidateNonnegativeQuantity(value, fldPath)...)
  6795  	if helper.IsIntegerResourceName(resource) {
  6796  		if value.MilliValue()%int64(1000) != int64(0) {
  6797  			allErrs = append(allErrs, field.Invalid(fldPath, value, isNotIntegerErrorMsg))
  6798  		}
  6799  	}
  6800  	return allErrs
  6801  }
  6802  
  6803  // ValidateResourceQuotaUpdate tests to see if the update is legal for an end user to make.
  6804  func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
  6805  	allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
  6806  	allErrs = append(allErrs, ValidateResourceQuotaSpec(&newResourceQuota.Spec, field.NewPath("spec"))...)
  6807  
  6808  	// ensure scopes cannot change, and that resources are still valid for scope
  6809  	fldPath := field.NewPath("spec", "scopes")
  6810  	oldScopes := sets.New[string]()
  6811  	newScopes := sets.New[string]()
  6812  	for _, scope := range newResourceQuota.Spec.Scopes {
  6813  		newScopes.Insert(string(scope))
  6814  	}
  6815  	for _, scope := range oldResourceQuota.Spec.Scopes {
  6816  		oldScopes.Insert(string(scope))
  6817  	}
  6818  	if !oldScopes.Equal(newScopes) {
  6819  		allErrs = append(allErrs, field.Invalid(fldPath, newResourceQuota.Spec.Scopes, fieldImmutableErrorMsg))
  6820  	}
  6821  
  6822  	return allErrs
  6823  }
  6824  
  6825  // ValidateResourceQuotaStatusUpdate tests to see if the status update is legal for an end user to make.
  6826  func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
  6827  	allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
  6828  	if len(newResourceQuota.ResourceVersion) == 0 {
  6829  		allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
  6830  	}
  6831  	fldPath := field.NewPath("status", "hard")
  6832  	for k, v := range newResourceQuota.Status.Hard {
  6833  		resPath := fldPath.Key(string(k))
  6834  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6835  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6836  	}
  6837  	fldPath = field.NewPath("status", "used")
  6838  	for k, v := range newResourceQuota.Status.Used {
  6839  		resPath := fldPath.Key(string(k))
  6840  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6841  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6842  	}
  6843  	return allErrs
  6844  }
  6845  
  6846  // ValidateNamespace tests if required fields are set.
  6847  func ValidateNamespace(namespace *core.Namespace) field.ErrorList {
  6848  	allErrs := ValidateObjectMeta(&namespace.ObjectMeta, false, ValidateNamespaceName, field.NewPath("metadata"))
  6849  	for i := range namespace.Spec.Finalizers {
  6850  		allErrs = append(allErrs, validateFinalizerName(string(namespace.Spec.Finalizers[i]), field.NewPath("spec", "finalizers"))...)
  6851  	}
  6852  	return allErrs
  6853  }
  6854  
  6855  // Validate finalizer names
  6856  func validateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
  6857  	allErrs := apimachineryvalidation.ValidateFinalizerName(stringValue, fldPath)
  6858  	allErrs = append(allErrs, validateKubeFinalizerName(stringValue, fldPath)...)
  6859  	return allErrs
  6860  }
  6861  
  6862  // validateKubeFinalizerName checks for "standard" names of legacy finalizer
  6863  func validateKubeFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
  6864  	allErrs := field.ErrorList{}
  6865  	if len(strings.Split(stringValue, "/")) == 1 {
  6866  		if !helper.IsStandardFinalizerName(stringValue) {
  6867  			return append(allErrs, field.Invalid(fldPath, stringValue, "name is neither a standard finalizer name nor is it fully qualified"))
  6868  		}
  6869  	}
  6870  
  6871  	return allErrs
  6872  }
  6873  
  6874  // ValidateNamespaceUpdate tests to make sure a namespace update can be applied.
  6875  func ValidateNamespaceUpdate(newNamespace *core.Namespace, oldNamespace *core.Namespace) field.ErrorList {
  6876  	allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  6877  	return allErrs
  6878  }
  6879  
  6880  // ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make.
  6881  func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
  6882  	allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  6883  	if newNamespace.DeletionTimestamp.IsZero() {
  6884  		if newNamespace.Status.Phase != core.NamespaceActive {
  6885  			allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Active' if `deletionTimestamp` is empty"))
  6886  		}
  6887  	} else {
  6888  		if newNamespace.Status.Phase != core.NamespaceTerminating {
  6889  			allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Terminating' if `deletionTimestamp` is not empty"))
  6890  		}
  6891  	}
  6892  	return allErrs
  6893  }
  6894  
  6895  // ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make.
  6896  func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
  6897  	allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  6898  
  6899  	fldPath := field.NewPath("spec", "finalizers")
  6900  	for i := range newNamespace.Spec.Finalizers {
  6901  		idxPath := fldPath.Index(i)
  6902  		allErrs = append(allErrs, validateFinalizerName(string(newNamespace.Spec.Finalizers[i]), idxPath)...)
  6903  	}
  6904  	return allErrs
  6905  }
  6906  
  6907  // ValidateEndpoints validates Endpoints on create and update.
  6908  func ValidateEndpoints(endpoints *core.Endpoints) field.ErrorList {
  6909  	allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
  6910  	allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
  6911  	allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...)
  6912  	return allErrs
  6913  }
  6914  
  6915  // ValidateEndpointsCreate validates Endpoints on create.
  6916  func ValidateEndpointsCreate(endpoints *core.Endpoints) field.ErrorList {
  6917  	return ValidateEndpoints(endpoints)
  6918  }
  6919  
  6920  // ValidateEndpointsUpdate validates Endpoints on update. NodeName changes are
  6921  // allowed during update to accommodate the case where nodeIP or PodCIDR is
  6922  // reused. An existing endpoint ip will have a different nodeName if this
  6923  // happens.
  6924  func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
  6925  	allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
  6926  	allErrs = append(allErrs, ValidateEndpoints(newEndpoints)...)
  6927  	return allErrs
  6928  }
  6929  
  6930  func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path) field.ErrorList {
  6931  	allErrs := field.ErrorList{}
  6932  	for i := range subsets {
  6933  		ss := &subsets[i]
  6934  		idxPath := fldPath.Index(i)
  6935  
  6936  		// EndpointSubsets must include endpoint address. For headless service, we allow its endpoints not to have ports.
  6937  		if len(ss.Addresses) == 0 && len(ss.NotReadyAddresses) == 0 {
  6938  			// TODO: consider adding a RequiredOneOf() error for this and similar cases
  6939  			allErrs = append(allErrs, field.Required(idxPath, "must specify `addresses` or `notReadyAddresses`"))
  6940  		}
  6941  		for addr := range ss.Addresses {
  6942  			allErrs = append(allErrs, validateEndpointAddress(&ss.Addresses[addr], idxPath.Child("addresses").Index(addr))...)
  6943  		}
  6944  		for addr := range ss.NotReadyAddresses {
  6945  			allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...)
  6946  		}
  6947  		for port := range ss.Ports {
  6948  			allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
  6949  		}
  6950  	}
  6951  
  6952  	return allErrs
  6953  }
  6954  
  6955  func validateEndpointAddress(address *core.EndpointAddress, fldPath *field.Path) field.ErrorList {
  6956  	allErrs := field.ErrorList{}
  6957  	for _, msg := range validation.IsValidIP(address.IP) {
  6958  		allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), address.IP, msg))
  6959  	}
  6960  	if len(address.Hostname) > 0 {
  6961  		allErrs = append(allErrs, ValidateDNS1123Label(address.Hostname, fldPath.Child("hostname"))...)
  6962  	}
  6963  	// During endpoint update, verify that NodeName is a DNS subdomain and transition rules allow the update
  6964  	if address.NodeName != nil {
  6965  		for _, msg := range ValidateNodeName(*address.NodeName, false) {
  6966  			allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), *address.NodeName, msg))
  6967  		}
  6968  	}
  6969  	allErrs = append(allErrs, ValidateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
  6970  	return allErrs
  6971  }
  6972  
  6973  // ValidateNonSpecialIP is used to validate Endpoints, EndpointSlices, and
  6974  // external IPs. Specifically, this disallows unspecified and loopback addresses
  6975  // are nonsensical and link-local addresses tend to be used for node-centric
  6976  // purposes (e.g. metadata service).
  6977  //
  6978  // IPv6 references
  6979  // - https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
  6980  // - https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
  6981  func ValidateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
  6982  	allErrs := field.ErrorList{}
  6983  	ip := netutils.ParseIPSloppy(ipAddress)
  6984  	if ip == nil {
  6985  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "must be a valid IP address"))
  6986  		return allErrs
  6987  	}
  6988  	if ip.IsUnspecified() {
  6989  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, fmt.Sprintf("may not be unspecified (%v)", ipAddress)))
  6990  	}
  6991  	if ip.IsLoopback() {
  6992  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the loopback range (127.0.0.0/8, ::1/128)"))
  6993  	}
  6994  	if ip.IsLinkLocalUnicast() {
  6995  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local range (169.254.0.0/16, fe80::/10)"))
  6996  	}
  6997  	if ip.IsLinkLocalMulticast() {
  6998  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local multicast range (224.0.0.0/24, ff02::/10)"))
  6999  	}
  7000  	return allErrs
  7001  }
  7002  
  7003  func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *field.Path) field.ErrorList {
  7004  	allErrs := field.ErrorList{}
  7005  	if requireName && len(port.Name) == 0 {
  7006  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  7007  	} else if len(port.Name) != 0 {
  7008  		allErrs = append(allErrs, ValidateDNS1123Label(port.Name, fldPath.Child("name"))...)
  7009  	}
  7010  	for _, msg := range validation.IsValidPortNum(int(port.Port)) {
  7011  		allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), port.Port, msg))
  7012  	}
  7013  	if len(port.Protocol) == 0 {
  7014  		allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
  7015  	} else if !supportedPortProtocols.Has(port.Protocol) {
  7016  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, sets.List(supportedPortProtocols)))
  7017  	}
  7018  	if port.AppProtocol != nil {
  7019  		allErrs = append(allErrs, ValidateQualifiedName(*port.AppProtocol, fldPath.Child("appProtocol"))...)
  7020  	}
  7021  	return allErrs
  7022  }
  7023  
  7024  // ValidateSecurityContext ensures the security context contains valid settings
  7025  func ValidateSecurityContext(sc *core.SecurityContext, fldPath *field.Path) field.ErrorList {
  7026  	allErrs := field.ErrorList{}
  7027  	// this should only be true for testing since SecurityContext is defaulted by the core
  7028  	if sc == nil {
  7029  		return allErrs
  7030  	}
  7031  
  7032  	if sc.Privileged != nil {
  7033  		if *sc.Privileged && !capabilities.Get().AllowPrivileged {
  7034  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("privileged"), "disallowed by cluster policy"))
  7035  		}
  7036  	}
  7037  
  7038  	if sc.RunAsUser != nil {
  7039  		for _, msg := range validation.IsValidUserID(*sc.RunAsUser) {
  7040  			allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *sc.RunAsUser, msg))
  7041  		}
  7042  	}
  7043  
  7044  	if sc.RunAsGroup != nil {
  7045  		for _, msg := range validation.IsValidGroupID(*sc.RunAsGroup) {
  7046  			allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsGroup"), *sc.RunAsGroup, msg))
  7047  		}
  7048  	}
  7049  
  7050  	if sc.ProcMount != nil {
  7051  		if err := ValidateProcMountType(fldPath.Child("procMount"), *sc.ProcMount); err != nil {
  7052  			allErrs = append(allErrs, err)
  7053  		}
  7054  
  7055  	}
  7056  	allErrs = append(allErrs, validateSeccompProfileField(sc.SeccompProfile, fldPath.Child("seccompProfile"))...)
  7057  	if sc.AllowPrivilegeEscalation != nil && !*sc.AllowPrivilegeEscalation {
  7058  		if sc.Privileged != nil && *sc.Privileged {
  7059  			allErrs = append(allErrs, field.Invalid(fldPath, sc, "cannot set `allowPrivilegeEscalation` to false and `privileged` to true"))
  7060  		}
  7061  
  7062  		if sc.Capabilities != nil {
  7063  			for _, cap := range sc.Capabilities.Add {
  7064  				if string(cap) == "CAP_SYS_ADMIN" {
  7065  					allErrs = append(allErrs, field.Invalid(fldPath, sc, "cannot set `allowPrivilegeEscalation` to false and `capabilities.Add` CAP_SYS_ADMIN"))
  7066  				}
  7067  			}
  7068  		}
  7069  	}
  7070  
  7071  	allErrs = append(allErrs, validateWindowsSecurityContextOptions(sc.WindowsOptions, fldPath.Child("windowsOptions"))...)
  7072  
  7073  	return allErrs
  7074  }
  7075  
  7076  // maxGMSACredentialSpecLength is the max length, in bytes, for the actual contents
  7077  // of a GMSA cred spec. In general, those shouldn't be more than a few hundred bytes,
  7078  // so we want to give plenty of room here while still providing an upper bound.
  7079  // The runAsUserName field will be used to execute the given container's entrypoint, and
  7080  // it can be formatted as "DOMAIN/USER", where the DOMAIN is optional, maxRunAsUserNameDomainLength
  7081  // is the max character length for the user's DOMAIN, and maxRunAsUserNameUserLength
  7082  // is the max character length for the USER itself. Both the DOMAIN and USER have their
  7083  // own restrictions, and more information about them can be found here:
  7084  // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
  7085  // https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/bb726984(v=technet.10)
  7086  const (
  7087  	maxGMSACredentialSpecLengthInKiB = 64
  7088  	maxGMSACredentialSpecLength      = maxGMSACredentialSpecLengthInKiB * 1024
  7089  	maxRunAsUserNameDomainLength     = 256
  7090  	maxRunAsUserNameUserLength       = 104
  7091  )
  7092  
  7093  var (
  7094  	// control characters are not permitted in the runAsUserName field.
  7095  	ctrlRegex = regexp.MustCompile(`[[:cntrl:]]+`)
  7096  
  7097  	// a valid NetBios Domain name cannot start with a dot, has at least 1 character,
  7098  	// at most 15 characters, and it cannot the characters: \ / : * ? " < > |
  7099  	validNetBiosRegex = regexp.MustCompile(`^[^\\/:\*\?"<>|\.][^\\/:\*\?"<>|]{0,14}$`)
  7100  
  7101  	// a valid DNS name contains only alphanumeric characters, dots, and dashes.
  7102  	dnsLabelFormat                 = `[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?`
  7103  	dnsSubdomainFormat             = fmt.Sprintf(`^%s(?:\.%s)*$`, dnsLabelFormat, dnsLabelFormat)
  7104  	validWindowsUserDomainDNSRegex = regexp.MustCompile(dnsSubdomainFormat)
  7105  
  7106  	// a username is invalid if it contains the characters: " / \ [ ] : ; | = , + * ? < > @
  7107  	// or it contains only dots or spaces.
  7108  	invalidUserNameCharsRegex      = regexp.MustCompile(`["/\\:;|=,\+\*\?<>@\[\]]`)
  7109  	invalidUserNameDotsSpacesRegex = regexp.MustCompile(`^[\. ]+$`)
  7110  )
  7111  
  7112  func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityContextOptions, fieldPath *field.Path) field.ErrorList {
  7113  	allErrs := field.ErrorList{}
  7114  
  7115  	if windowsOptions == nil {
  7116  		return allErrs
  7117  	}
  7118  
  7119  	if windowsOptions.GMSACredentialSpecName != nil {
  7120  		// gmsaCredentialSpecName must be the name of a custom resource
  7121  		for _, msg := range validation.IsDNS1123Subdomain(*windowsOptions.GMSACredentialSpecName) {
  7122  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpecName"), windowsOptions.GMSACredentialSpecName, msg))
  7123  		}
  7124  	}
  7125  
  7126  	if windowsOptions.GMSACredentialSpec != nil {
  7127  		if l := len(*windowsOptions.GMSACredentialSpec); l == 0 {
  7128  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpec"), windowsOptions.GMSACredentialSpec, "gmsaCredentialSpec cannot be an empty string"))
  7129  		} else if l > maxGMSACredentialSpecLength {
  7130  			errMsg := fmt.Sprintf("gmsaCredentialSpec size must be under %d KiB", maxGMSACredentialSpecLengthInKiB)
  7131  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpec"), windowsOptions.GMSACredentialSpec, errMsg))
  7132  		}
  7133  	}
  7134  
  7135  	if windowsOptions.RunAsUserName != nil {
  7136  		if l := len(*windowsOptions.RunAsUserName); l == 0 {
  7137  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, "runAsUserName cannot be an empty string"))
  7138  		} else if ctrlRegex.MatchString(*windowsOptions.RunAsUserName) {
  7139  			errMsg := "runAsUserName cannot contain control characters"
  7140  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7141  		} else if parts := strings.Split(*windowsOptions.RunAsUserName, "\\"); len(parts) > 2 {
  7142  			errMsg := "runAsUserName cannot contain more than one backslash"
  7143  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7144  		} else {
  7145  			var (
  7146  				hasDomain = false
  7147  				domain    = ""
  7148  				user      string
  7149  			)
  7150  			if len(parts) == 1 {
  7151  				user = parts[0]
  7152  			} else {
  7153  				hasDomain = true
  7154  				domain = parts[0]
  7155  				user = parts[1]
  7156  			}
  7157  
  7158  			if len(domain) >= maxRunAsUserNameDomainLength {
  7159  				errMsg := fmt.Sprintf("runAsUserName's Domain length must be under %d characters", maxRunAsUserNameDomainLength)
  7160  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7161  			}
  7162  
  7163  			if hasDomain && !(validNetBiosRegex.MatchString(domain) || validWindowsUserDomainDNSRegex.MatchString(domain)) {
  7164  				errMsg := "runAsUserName's Domain doesn't match the NetBios nor the DNS format"
  7165  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7166  			}
  7167  
  7168  			if l := len(user); l == 0 {
  7169  				errMsg := "runAsUserName's User cannot be empty"
  7170  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7171  			} else if l > maxRunAsUserNameUserLength {
  7172  				errMsg := fmt.Sprintf("runAsUserName's User length must not be longer than %d characters", maxRunAsUserNameUserLength)
  7173  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7174  			}
  7175  
  7176  			if invalidUserNameDotsSpacesRegex.MatchString(user) {
  7177  				errMsg := `runAsUserName's User cannot contain only periods or spaces`
  7178  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7179  			}
  7180  
  7181  			if invalidUserNameCharsRegex.MatchString(user) {
  7182  				errMsg := `runAsUserName's User cannot contain the following characters: "/\:;|=,+*?<>@[]`
  7183  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7184  			}
  7185  		}
  7186  	}
  7187  
  7188  	return allErrs
  7189  }
  7190  
  7191  func validateWindowsHostProcessPod(podSpec *core.PodSpec, fieldPath *field.Path) field.ErrorList {
  7192  	allErrs := field.ErrorList{}
  7193  
  7194  	// Keep track of container and hostProcess container count for validate
  7195  	containerCount := 0
  7196  	hostProcessContainerCount := 0
  7197  
  7198  	var podHostProcess *bool
  7199  	if podSpec.SecurityContext != nil && podSpec.SecurityContext.WindowsOptions != nil {
  7200  		podHostProcess = podSpec.SecurityContext.WindowsOptions.HostProcess
  7201  	}
  7202  
  7203  	hostNetwork := false
  7204  	if podSpec.SecurityContext != nil {
  7205  		hostNetwork = podSpec.SecurityContext.HostNetwork
  7206  	}
  7207  
  7208  	podshelper.VisitContainersWithPath(podSpec, fieldPath, func(c *core.Container, cFieldPath *field.Path) bool {
  7209  		containerCount++
  7210  
  7211  		var containerHostProcess *bool = nil
  7212  		if c.SecurityContext != nil && c.SecurityContext.WindowsOptions != nil {
  7213  			containerHostProcess = c.SecurityContext.WindowsOptions.HostProcess
  7214  		}
  7215  
  7216  		if podHostProcess != nil && containerHostProcess != nil && *podHostProcess != *containerHostProcess {
  7217  			errMsg := fmt.Sprintf("pod hostProcess value must be identical if both are specified, was %v", *podHostProcess)
  7218  			allErrs = append(allErrs, field.Invalid(cFieldPath.Child("securityContext", "windowsOptions", "hostProcess"), *containerHostProcess, errMsg))
  7219  		}
  7220  
  7221  		switch {
  7222  		case containerHostProcess != nil && *containerHostProcess:
  7223  			// Container explicitly sets hostProcess=true
  7224  			hostProcessContainerCount++
  7225  		case containerHostProcess == nil && podHostProcess != nil && *podHostProcess:
  7226  			// Container inherits hostProcess=true from pod settings
  7227  			hostProcessContainerCount++
  7228  		}
  7229  
  7230  		return true
  7231  	})
  7232  
  7233  	if hostProcessContainerCount > 0 {
  7234  		// At present, if a Windows Pods contains any HostProcess containers than all containers must be
  7235  		// HostProcess containers (explicitly set or inherited).
  7236  		if hostProcessContainerCount != containerCount {
  7237  			errMsg := "If pod contains any hostProcess containers then all containers must be HostProcess containers"
  7238  			allErrs = append(allErrs, field.Invalid(fieldPath, "", errMsg))
  7239  		}
  7240  
  7241  		// At present Windows Pods which contain HostProcess containers must also set HostNetwork.
  7242  		if !hostNetwork {
  7243  			errMsg := "hostNetwork must be true if pod contains any hostProcess containers"
  7244  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("hostNetwork"), hostNetwork, errMsg))
  7245  		}
  7246  
  7247  		if !capabilities.Get().AllowPrivileged {
  7248  			errMsg := "hostProcess containers are disallowed by cluster policy"
  7249  			allErrs = append(allErrs, field.Forbidden(fieldPath, errMsg))
  7250  		}
  7251  	}
  7252  
  7253  	return allErrs
  7254  }
  7255  
  7256  // validateOS validates the OS field within pod spec
  7257  func validateOS(podSpec *core.PodSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  7258  	allErrs := field.ErrorList{}
  7259  	os := podSpec.OS
  7260  	if os == nil {
  7261  		return allErrs
  7262  	}
  7263  	if len(os.Name) == 0 {
  7264  		return append(allErrs, field.Required(fldPath.Child("name"), "cannot be empty"))
  7265  	}
  7266  	if !validOS.Has(os.Name) {
  7267  		allErrs = append(allErrs, field.NotSupported(fldPath, os.Name, sets.List(validOS)))
  7268  	}
  7269  	return allErrs
  7270  }
  7271  
  7272  func ValidatePodLogOptions(opts *core.PodLogOptions) field.ErrorList {
  7273  	allErrs := field.ErrorList{}
  7274  	if opts.TailLines != nil && *opts.TailLines < 0 {
  7275  		allErrs = append(allErrs, field.Invalid(field.NewPath("tailLines"), *opts.TailLines, isNegativeErrorMsg))
  7276  	}
  7277  	if opts.LimitBytes != nil && *opts.LimitBytes < 1 {
  7278  		allErrs = append(allErrs, field.Invalid(field.NewPath("limitBytes"), *opts.LimitBytes, "must be greater than 0"))
  7279  	}
  7280  	switch {
  7281  	case opts.SinceSeconds != nil && opts.SinceTime != nil:
  7282  		allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "at most one of `sinceTime` or `sinceSeconds` may be specified"))
  7283  	case opts.SinceSeconds != nil:
  7284  		if *opts.SinceSeconds < 1 {
  7285  			allErrs = append(allErrs, field.Invalid(field.NewPath("sinceSeconds"), *opts.SinceSeconds, "must be greater than 0"))
  7286  		}
  7287  	}
  7288  	return allErrs
  7289  }
  7290  
  7291  var (
  7292  	supportedLoadBalancerIPMode = sets.New(core.LoadBalancerIPModeVIP, core.LoadBalancerIPModeProxy)
  7293  )
  7294  
  7295  // ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus
  7296  func ValidateLoadBalancerStatus(status *core.LoadBalancerStatus, fldPath *field.Path, spec *core.ServiceSpec) field.ErrorList {
  7297  	allErrs := field.ErrorList{}
  7298  	ingrPath := fldPath.Child("ingress")
  7299  	if !utilfeature.DefaultFeatureGate.Enabled(features.AllowServiceLBStatusOnNonLB) && spec.Type != core.ServiceTypeLoadBalancer && len(status.Ingress) != 0 {
  7300  		allErrs = append(allErrs, field.Forbidden(ingrPath, "may only be used when `spec.type` is 'LoadBalancer'"))
  7301  	} else {
  7302  		for i, ingress := range status.Ingress {
  7303  			idxPath := ingrPath.Index(i)
  7304  			if len(ingress.IP) > 0 {
  7305  				if isIP := (netutils.ParseIPSloppy(ingress.IP) != nil); !isIP {
  7306  					allErrs = append(allErrs, field.Invalid(idxPath.Child("ip"), ingress.IP, "must be a valid IP address"))
  7307  				}
  7308  			}
  7309  
  7310  			if utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) && ingress.IPMode == nil {
  7311  				if len(ingress.IP) > 0 {
  7312  					allErrs = append(allErrs, field.Required(idxPath.Child("ipMode"), "must be specified when `ip` is set"))
  7313  				}
  7314  			} else if ingress.IPMode != nil && len(ingress.IP) == 0 {
  7315  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("ipMode"), "may not be specified when `ip` is not set"))
  7316  			} else if ingress.IPMode != nil && !supportedLoadBalancerIPMode.Has(*ingress.IPMode) {
  7317  				allErrs = append(allErrs, field.NotSupported(idxPath.Child("ipMode"), ingress.IPMode, sets.List(supportedLoadBalancerIPMode)))
  7318  			}
  7319  
  7320  			if len(ingress.Hostname) > 0 {
  7321  				for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) {
  7322  					allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
  7323  				}
  7324  				if isIP := (netutils.ParseIPSloppy(ingress.Hostname) != nil); isIP {
  7325  					allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))
  7326  				}
  7327  			}
  7328  		}
  7329  	}
  7330  	return allErrs
  7331  }
  7332  
  7333  // validateVolumeNodeAffinity tests that the PersistentVolume.NodeAffinity has valid data
  7334  // returns:
  7335  // - true if volumeNodeAffinity is set
  7336  // - errorList if there are validation errors
  7337  func validateVolumeNodeAffinity(nodeAffinity *core.VolumeNodeAffinity, fldPath *field.Path) (bool, field.ErrorList) {
  7338  	allErrs := field.ErrorList{}
  7339  
  7340  	if nodeAffinity == nil {
  7341  		return false, allErrs
  7342  	}
  7343  
  7344  	if nodeAffinity.Required != nil {
  7345  		allErrs = append(allErrs, ValidateNodeSelector(nodeAffinity.Required, fldPath.Child("required"))...)
  7346  	} else {
  7347  		allErrs = append(allErrs, field.Required(fldPath.Child("required"), "must specify required node constraints"))
  7348  	}
  7349  
  7350  	return true, allErrs
  7351  }
  7352  
  7353  // ValidateCIDR validates whether a CIDR matches the conventions expected by net.ParseCIDR
  7354  func ValidateCIDR(cidr string) (*net.IPNet, error) {
  7355  	_, net, err := netutils.ParseCIDRSloppy(cidr)
  7356  	if err != nil {
  7357  		return nil, err
  7358  	}
  7359  	return net, nil
  7360  }
  7361  
  7362  func IsDecremented(update, old *int32) bool {
  7363  	if update == nil && old != nil {
  7364  		return true
  7365  	}
  7366  	if update == nil || old == nil {
  7367  		return false
  7368  	}
  7369  	return *update < *old
  7370  }
  7371  
  7372  // ValidateProcMountType tests that the argument is a valid ProcMountType.
  7373  func ValidateProcMountType(fldPath *field.Path, procMountType core.ProcMountType) *field.Error {
  7374  	switch procMountType {
  7375  	case core.DefaultProcMount, core.UnmaskedProcMount:
  7376  		return nil
  7377  	default:
  7378  		return field.NotSupported(fldPath, procMountType, []core.ProcMountType{core.DefaultProcMount, core.UnmaskedProcMount})
  7379  	}
  7380  }
  7381  
  7382  var (
  7383  	supportedScheduleActions = sets.New(core.DoNotSchedule, core.ScheduleAnyway)
  7384  )
  7385  
  7386  // validateTopologySpreadConstraints validates given TopologySpreadConstraints.
  7387  func validateTopologySpreadConstraints(constraints []core.TopologySpreadConstraint, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  7388  	allErrs := field.ErrorList{}
  7389  
  7390  	for i, constraint := range constraints {
  7391  		subFldPath := fldPath.Index(i)
  7392  		if err := ValidateMaxSkew(subFldPath.Child("maxSkew"), constraint.MaxSkew); err != nil {
  7393  			allErrs = append(allErrs, err)
  7394  		}
  7395  		if err := ValidateTopologyKey(subFldPath.Child("topologyKey"), constraint.TopologyKey); err != nil {
  7396  			allErrs = append(allErrs, err)
  7397  		}
  7398  		if err := ValidateWhenUnsatisfiable(subFldPath.Child("whenUnsatisfiable"), constraint.WhenUnsatisfiable); err != nil {
  7399  			allErrs = append(allErrs, err)
  7400  		}
  7401  		// tuple {topologyKey, whenUnsatisfiable} denotes one kind of spread constraint
  7402  		if err := ValidateSpreadConstraintNotRepeat(subFldPath.Child("{topologyKey, whenUnsatisfiable}"), constraint, constraints[i+1:]); err != nil {
  7403  			allErrs = append(allErrs, err)
  7404  		}
  7405  		allErrs = append(allErrs, validateMinDomains(subFldPath.Child("minDomains"), constraint.MinDomains, constraint.WhenUnsatisfiable)...)
  7406  		if err := validateNodeInclusionPolicy(subFldPath.Child("nodeAffinityPolicy"), constraint.NodeAffinityPolicy); err != nil {
  7407  			allErrs = append(allErrs, err)
  7408  		}
  7409  		if err := validateNodeInclusionPolicy(subFldPath.Child("nodeTaintsPolicy"), constraint.NodeTaintsPolicy); err != nil {
  7410  			allErrs = append(allErrs, err)
  7411  		}
  7412  		allErrs = append(allErrs, validateMatchLabelKeysInTopologySpread(subFldPath.Child("matchLabelKeys"), constraint.MatchLabelKeys, constraint.LabelSelector)...)
  7413  		if !opts.AllowInvalidTopologySpreadConstraintLabelSelector {
  7414  			allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(constraint.LabelSelector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, subFldPath.Child("labelSelector"))...)
  7415  		}
  7416  	}
  7417  
  7418  	return allErrs
  7419  }
  7420  
  7421  // ValidateMaxSkew tests that the argument is a valid MaxSkew.
  7422  func ValidateMaxSkew(fldPath *field.Path, maxSkew int32) *field.Error {
  7423  	if maxSkew <= 0 {
  7424  		return field.Invalid(fldPath, maxSkew, isNotPositiveErrorMsg)
  7425  	}
  7426  	return nil
  7427  }
  7428  
  7429  // validateMinDomains tests that the argument is a valid MinDomains.
  7430  func validateMinDomains(fldPath *field.Path, minDomains *int32, action core.UnsatisfiableConstraintAction) field.ErrorList {
  7431  	if minDomains == nil {
  7432  		return nil
  7433  	}
  7434  	var allErrs field.ErrorList
  7435  	if *minDomains <= 0 {
  7436  		allErrs = append(allErrs, field.Invalid(fldPath, minDomains, isNotPositiveErrorMsg))
  7437  	}
  7438  	// When MinDomains is non-nil, whenUnsatisfiable must be DoNotSchedule.
  7439  	if action != core.DoNotSchedule {
  7440  		allErrs = append(allErrs, field.Invalid(fldPath, minDomains, fmt.Sprintf("can only use minDomains if whenUnsatisfiable=%s, not %s", core.DoNotSchedule, action)))
  7441  	}
  7442  	return allErrs
  7443  }
  7444  
  7445  // ValidateTopologyKey tests that the argument is a valid TopologyKey.
  7446  func ValidateTopologyKey(fldPath *field.Path, topologyKey string) *field.Error {
  7447  	if len(topologyKey) == 0 {
  7448  		return field.Required(fldPath, "can not be empty")
  7449  	}
  7450  	return nil
  7451  }
  7452  
  7453  // ValidateWhenUnsatisfiable tests that the argument is a valid UnsatisfiableConstraintAction.
  7454  func ValidateWhenUnsatisfiable(fldPath *field.Path, action core.UnsatisfiableConstraintAction) *field.Error {
  7455  	if !supportedScheduleActions.Has(action) {
  7456  		return field.NotSupported(fldPath, action, sets.List(supportedScheduleActions))
  7457  	}
  7458  	return nil
  7459  }
  7460  
  7461  // ValidateSpreadConstraintNotRepeat tests that if `constraint` duplicates with `existingConstraintPairs`
  7462  // on TopologyKey and WhenUnsatisfiable fields.
  7463  func ValidateSpreadConstraintNotRepeat(fldPath *field.Path, constraint core.TopologySpreadConstraint, restingConstraints []core.TopologySpreadConstraint) *field.Error {
  7464  	for _, restingConstraint := range restingConstraints {
  7465  		if constraint.TopologyKey == restingConstraint.TopologyKey &&
  7466  			constraint.WhenUnsatisfiable == restingConstraint.WhenUnsatisfiable {
  7467  			return field.Duplicate(fldPath, fmt.Sprintf("{%v, %v}", constraint.TopologyKey, constraint.WhenUnsatisfiable))
  7468  		}
  7469  	}
  7470  	return nil
  7471  }
  7472  
  7473  var (
  7474  	supportedPodTopologySpreadNodePolicies = sets.New(core.NodeInclusionPolicyIgnore, core.NodeInclusionPolicyHonor)
  7475  )
  7476  
  7477  // validateNodeAffinityPolicy tests that the argument is a valid NodeInclusionPolicy.
  7478  func validateNodeInclusionPolicy(fldPath *field.Path, policy *core.NodeInclusionPolicy) *field.Error {
  7479  	if policy == nil {
  7480  		return nil
  7481  	}
  7482  
  7483  	if !supportedPodTopologySpreadNodePolicies.Has(*policy) {
  7484  		return field.NotSupported(fldPath, policy, sets.List(supportedPodTopologySpreadNodePolicies))
  7485  	}
  7486  	return nil
  7487  }
  7488  
  7489  // validateMatchLabelKeysAndMismatchLabelKeys checks if both matchLabelKeys and mismatchLabelKeys are valid.
  7490  // - validate that all matchLabelKeys and mismatchLabelKeys are valid label names.
  7491  // - validate that the user doens't specify the same key in both matchLabelKeys and labelSelector.
  7492  // - validate that any matchLabelKeys are not duplicated with mismatchLabelKeys.
  7493  func validateMatchLabelKeysAndMismatchLabelKeys(fldPath *field.Path, matchLabelKeys, mismatchLabelKeys []string, labelSelector *metav1.LabelSelector) field.ErrorList {
  7494  	var allErrs field.ErrorList
  7495  	// 1. validate that all matchLabelKeys and mismatchLabelKeys are valid label names.
  7496  	allErrs = append(allErrs, validateLabelKeys(fldPath.Child("matchLabelKeys"), matchLabelKeys, labelSelector)...)
  7497  	allErrs = append(allErrs, validateLabelKeys(fldPath.Child("mismatchLabelKeys"), mismatchLabelKeys, labelSelector)...)
  7498  
  7499  	// 2. validate that the user doens't specify the same key in both matchLabelKeys and labelSelector.
  7500  	// It doesn't make sense to have the labelselector with the key specified in matchLabelKeys
  7501  	// because the matchLabelKeys will be `In` labelSelector which matches with only one value in the key
  7502  	// and we cannot make any further filtering with that key.
  7503  	// On the other hand, we may want to have labelSelector with the key specified in mismatchLabelKeys.
  7504  	// because the mismatchLabelKeys will be `NotIn` labelSelector
  7505  	// and we may want to filter Pods further with other labelSelector with that key.
  7506  
  7507  	// labelKeysMap is keyed by label key and valued by the index of label key in labelKeys.
  7508  	if labelSelector != nil {
  7509  		labelKeysMap := map[string]int{}
  7510  		for i, key := range matchLabelKeys {
  7511  			labelKeysMap[key] = i
  7512  		}
  7513  		labelSelectorKeys := sets.New[string]()
  7514  		for key := range labelSelector.MatchLabels {
  7515  			labelSelectorKeys.Insert(key)
  7516  		}
  7517  		for _, matchExpression := range labelSelector.MatchExpressions {
  7518  			key := matchExpression.Key
  7519  			if i, ok := labelKeysMap[key]; ok && labelSelectorKeys.Has(key) {
  7520  				// Before validateLabelKeysWithSelector is called, the labelSelector has already got the selector created from matchLabelKeys.
  7521  				// Here, we found the duplicate key in labelSelector and the key is specified in labelKeys.
  7522  				// Meaning that the same key is specified in both labelSelector and matchLabelKeys/mismatchLabelKeys.
  7523  				allErrs = append(allErrs, field.Invalid(fldPath.Index(i), key, "exists in both matchLabelKeys and labelSelector"))
  7524  			}
  7525  
  7526  			labelSelectorKeys.Insert(key)
  7527  		}
  7528  	}
  7529  
  7530  	// 3. validate that any matchLabelKeys are not duplicated with mismatchLabelKeys.
  7531  	mismatchLabelKeysSet := sets.New(mismatchLabelKeys...)
  7532  	for i, k := range matchLabelKeys {
  7533  		if mismatchLabelKeysSet.Has(k) {
  7534  			allErrs = append(allErrs, field.Invalid(fldPath.Child("matchLabelKeys").Index(i), k, "exists in both matchLabelKeys and mismatchLabelKeys"))
  7535  		}
  7536  	}
  7537  
  7538  	return allErrs
  7539  }
  7540  
  7541  // validateMatchLabelKeysInTopologySpread tests that the elements are a valid label name and are not already included in labelSelector.
  7542  func validateMatchLabelKeysInTopologySpread(fldPath *field.Path, matchLabelKeys []string, labelSelector *metav1.LabelSelector) field.ErrorList {
  7543  	if len(matchLabelKeys) == 0 {
  7544  		return nil
  7545  	}
  7546  
  7547  	var allErrs field.ErrorList
  7548  	labelSelectorKeys := sets.Set[string]{}
  7549  
  7550  	if labelSelector != nil {
  7551  		for key := range labelSelector.MatchLabels {
  7552  			labelSelectorKeys.Insert(key)
  7553  		}
  7554  		for _, matchExpression := range labelSelector.MatchExpressions {
  7555  			labelSelectorKeys.Insert(matchExpression.Key)
  7556  		}
  7557  	} else {
  7558  		allErrs = append(allErrs, field.Forbidden(fldPath, "must not be specified when labelSelector is not set"))
  7559  	}
  7560  
  7561  	for i, key := range matchLabelKeys {
  7562  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(key, fldPath.Index(i))...)
  7563  		if labelSelectorKeys.Has(key) {
  7564  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i), key, "exists in both matchLabelKeys and labelSelector"))
  7565  		}
  7566  	}
  7567  
  7568  	return allErrs
  7569  }
  7570  
  7571  // validateLabelKeys tests that the label keys are a valid label name.
  7572  // It's intended to be used for matchLabelKeys or mismatchLabelKeys.
  7573  func validateLabelKeys(fldPath *field.Path, labelKeys []string, labelSelector *metav1.LabelSelector) field.ErrorList {
  7574  	if len(labelKeys) == 0 {
  7575  		return nil
  7576  	}
  7577  
  7578  	if labelSelector == nil {
  7579  		return field.ErrorList{field.Forbidden(fldPath, "must not be specified when labelSelector is not set")}
  7580  	}
  7581  
  7582  	var allErrs field.ErrorList
  7583  	for i, key := range labelKeys {
  7584  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(key, fldPath.Index(i))...)
  7585  	}
  7586  
  7587  	return allErrs
  7588  }
  7589  
  7590  // ValidateServiceClusterIPsRelatedFields validates .spec.ClusterIPs,,
  7591  // .spec.IPFamilies, .spec.ipFamilyPolicy.  This is exported because it is used
  7592  // during IP init and allocation.
  7593  func ValidateServiceClusterIPsRelatedFields(service *core.Service) field.ErrorList {
  7594  	// ClusterIP, ClusterIPs, IPFamilyPolicy and IPFamilies are validated prior (all must be unset) for ExternalName service
  7595  	if service.Spec.Type == core.ServiceTypeExternalName {
  7596  		return field.ErrorList{}
  7597  	}
  7598  
  7599  	allErrs := field.ErrorList{}
  7600  	hasInvalidIPs := false
  7601  
  7602  	specPath := field.NewPath("spec")
  7603  	clusterIPsField := specPath.Child("clusterIPs")
  7604  	ipFamiliesField := specPath.Child("ipFamilies")
  7605  	ipFamilyPolicyField := specPath.Child("ipFamilyPolicy")
  7606  
  7607  	// Make sure ClusterIP and ClusterIPs are synced.  For most cases users can
  7608  	// just manage one or the other and we'll handle the rest (see PrepareFor*
  7609  	// in strategy).
  7610  	if len(service.Spec.ClusterIP) != 0 {
  7611  		// If ClusterIP is set, ClusterIPs[0] must match.
  7612  		if len(service.Spec.ClusterIPs) == 0 {
  7613  			allErrs = append(allErrs, field.Required(clusterIPsField, ""))
  7614  		} else if service.Spec.ClusterIPs[0] != service.Spec.ClusterIP {
  7615  			allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "first value must match `clusterIP`"))
  7616  		}
  7617  	} else { // ClusterIP == ""
  7618  		// If ClusterIP is not set, ClusterIPs must also be unset.
  7619  		if len(service.Spec.ClusterIPs) != 0 {
  7620  			allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "must be empty when `clusterIP` is not specified"))
  7621  		}
  7622  	}
  7623  
  7624  	// ipfamilies stand alone validation
  7625  	// must be either IPv4 or IPv6
  7626  	seen := sets.Set[core.IPFamily]{}
  7627  	for i, ipFamily := range service.Spec.IPFamilies {
  7628  		if !supportedServiceIPFamily.Has(ipFamily) {
  7629  			allErrs = append(allErrs, field.NotSupported(ipFamiliesField.Index(i), ipFamily, sets.List(supportedServiceIPFamily)))
  7630  		}
  7631  		// no duplicate check also ensures that ipfamilies is dualstacked, in any order
  7632  		if seen.Has(ipFamily) {
  7633  			allErrs = append(allErrs, field.Duplicate(ipFamiliesField.Index(i), ipFamily))
  7634  		}
  7635  		seen.Insert(ipFamily)
  7636  	}
  7637  
  7638  	// IPFamilyPolicy stand alone validation
  7639  	// note: nil is ok, defaulted in alloc check registry/core/service/*
  7640  	if service.Spec.IPFamilyPolicy != nil {
  7641  		// must have a supported value
  7642  		if !supportedServiceIPFamilyPolicy.Has(*(service.Spec.IPFamilyPolicy)) {
  7643  			allErrs = append(allErrs, field.NotSupported(ipFamilyPolicyField, service.Spec.IPFamilyPolicy, sets.List(supportedServiceIPFamilyPolicy)))
  7644  		}
  7645  	}
  7646  
  7647  	// clusterIPs stand alone validation
  7648  	// valid ips with None and empty string handling
  7649  	// duplication check is done as part of DualStackvalidation below
  7650  	for i, clusterIP := range service.Spec.ClusterIPs {
  7651  		// valid at first location only. if and only if len(clusterIPs) == 1
  7652  		if i == 0 && clusterIP == core.ClusterIPNone {
  7653  			if len(service.Spec.ClusterIPs) > 1 {
  7654  				hasInvalidIPs = true
  7655  				allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "'None' must be the first and only value"))
  7656  			}
  7657  			continue
  7658  		}
  7659  
  7660  		// is it valid ip?
  7661  		errorMessages := validation.IsValidIP(clusterIP)
  7662  		hasInvalidIPs = (len(errorMessages) != 0) || hasInvalidIPs
  7663  		for _, msg := range errorMessages {
  7664  			allErrs = append(allErrs, field.Invalid(clusterIPsField.Index(i), clusterIP, msg))
  7665  		}
  7666  	}
  7667  
  7668  	// max two
  7669  	if len(service.Spec.ClusterIPs) > 2 {
  7670  		allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "may only hold up to 2 values"))
  7671  	}
  7672  
  7673  	// at this stage if there is an invalid ip or misplaced none/empty string
  7674  	// it will skew the error messages (bad index || dualstackness of already bad ips). so we
  7675  	// stop here if there are errors in clusterIPs validation
  7676  	if hasInvalidIPs {
  7677  		return allErrs
  7678  	}
  7679  
  7680  	// must be dual stacked ips if they are more than one ip
  7681  	if len(service.Spec.ClusterIPs) > 1 /* meaning: it does not have a None or empty string */ {
  7682  		dualStack, err := netutils.IsDualStackIPStrings(service.Spec.ClusterIPs)
  7683  		if err != nil { // though we check for that earlier. safe > sorry
  7684  			allErrs = append(allErrs, field.InternalError(clusterIPsField, fmt.Errorf("failed to check for dual stack with error:%v", err)))
  7685  		}
  7686  
  7687  		// We only support one from each IP family (i.e. max two IPs in this list).
  7688  		if !dualStack {
  7689  			allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "may specify no more than one IP for each IP family"))
  7690  		}
  7691  	}
  7692  
  7693  	// match clusterIPs to their families, if they were provided
  7694  	if !isHeadlessService(service) && len(service.Spec.ClusterIPs) > 0 && len(service.Spec.IPFamilies) > 0 {
  7695  		for i, ip := range service.Spec.ClusterIPs {
  7696  			if i > (len(service.Spec.IPFamilies) - 1) {
  7697  				break // no more families to check
  7698  			}
  7699  
  7700  			// 4=>6
  7701  			if service.Spec.IPFamilies[i] == core.IPv4Protocol && netutils.IsIPv6String(ip) {
  7702  				allErrs = append(allErrs, field.Invalid(clusterIPsField.Index(i), ip, fmt.Sprintf("expected an IPv4 value as indicated by `ipFamilies[%v]`", i)))
  7703  			}
  7704  			// 6=>4
  7705  			if service.Spec.IPFamilies[i] == core.IPv6Protocol && !netutils.IsIPv6String(ip) {
  7706  				allErrs = append(allErrs, field.Invalid(clusterIPsField.Index(i), ip, fmt.Sprintf("expected an IPv6 value as indicated by `ipFamilies[%v]`", i)))
  7707  			}
  7708  		}
  7709  	}
  7710  
  7711  	return allErrs
  7712  }
  7713  
  7714  // specific validation for clusterIPs in cases of user upgrading or downgrading to/from dualstack
  7715  func validateUpgradeDowngradeClusterIPs(oldService, service *core.Service) field.ErrorList {
  7716  	allErrs := make(field.ErrorList, 0)
  7717  
  7718  	// bail out early for ExternalName
  7719  	if service.Spec.Type == core.ServiceTypeExternalName || oldService.Spec.Type == core.ServiceTypeExternalName {
  7720  		return allErrs
  7721  	}
  7722  	newIsHeadless := isHeadlessService(service)
  7723  	oldIsHeadless := isHeadlessService(oldService)
  7724  
  7725  	if oldIsHeadless && newIsHeadless {
  7726  		return allErrs
  7727  	}
  7728  
  7729  	switch {
  7730  	// no change in ClusterIP lengths
  7731  	// compare each
  7732  	case len(oldService.Spec.ClusterIPs) == len(service.Spec.ClusterIPs):
  7733  		for i, ip := range oldService.Spec.ClusterIPs {
  7734  			if ip != service.Spec.ClusterIPs[i] {
  7735  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(i), service.Spec.ClusterIPs, "may not change once set"))
  7736  			}
  7737  		}
  7738  
  7739  	// something has been released (downgraded)
  7740  	case len(oldService.Spec.ClusterIPs) > len(service.Spec.ClusterIPs):
  7741  		// primary ClusterIP has been released
  7742  		if len(service.Spec.ClusterIPs) == 0 {
  7743  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(0), service.Spec.ClusterIPs, "primary clusterIP can not be unset"))
  7744  		}
  7745  
  7746  		// test if primary clusterIP has changed
  7747  		if len(oldService.Spec.ClusterIPs) > 0 &&
  7748  			len(service.Spec.ClusterIPs) > 0 &&
  7749  			service.Spec.ClusterIPs[0] != oldService.Spec.ClusterIPs[0] {
  7750  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7751  		}
  7752  
  7753  		// test if secondary ClusterIP has been released. has this service been downgraded correctly?
  7754  		// user *must* set IPFamilyPolicy == SingleStack
  7755  		if len(service.Spec.ClusterIPs) == 1 {
  7756  			if service.Spec.IPFamilyPolicy == nil || *(service.Spec.IPFamilyPolicy) != core.IPFamilyPolicySingleStack {
  7757  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilyPolicy"), service.Spec.IPFamilyPolicy, "must be set to 'SingleStack' when releasing the secondary clusterIP"))
  7758  			}
  7759  		}
  7760  	case len(oldService.Spec.ClusterIPs) < len(service.Spec.ClusterIPs):
  7761  		// something has been added (upgraded)
  7762  		// test if primary clusterIP has changed
  7763  		if len(oldService.Spec.ClusterIPs) > 0 &&
  7764  			service.Spec.ClusterIPs[0] != oldService.Spec.ClusterIPs[0] {
  7765  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7766  		}
  7767  		// we don't check for Policy == RequireDualStack here since, Validation/Creation func takes care of it
  7768  	}
  7769  	return allErrs
  7770  }
  7771  
  7772  // specific validation for ipFamilies in cases of user upgrading or downgrading to/from dualstack
  7773  func validateUpgradeDowngradeIPFamilies(oldService, service *core.Service) field.ErrorList {
  7774  	allErrs := make(field.ErrorList, 0)
  7775  	// bail out early for ExternalName
  7776  	if service.Spec.Type == core.ServiceTypeExternalName || oldService.Spec.Type == core.ServiceTypeExternalName {
  7777  		return allErrs
  7778  	}
  7779  
  7780  	oldIsHeadless := isHeadlessService(oldService)
  7781  	newIsHeadless := isHeadlessService(service)
  7782  
  7783  	// if changed to/from headless, then bail out
  7784  	if newIsHeadless != oldIsHeadless {
  7785  		return allErrs
  7786  	}
  7787  	// headless can change families
  7788  	if newIsHeadless {
  7789  		return allErrs
  7790  	}
  7791  
  7792  	switch {
  7793  	case len(oldService.Spec.IPFamilies) == len(service.Spec.IPFamilies):
  7794  		// no change in ClusterIP lengths
  7795  		// compare each
  7796  
  7797  		for i, ip := range oldService.Spec.IPFamilies {
  7798  			if ip != service.Spec.IPFamilies[i] {
  7799  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.IPFamilies, "may not change once set"))
  7800  			}
  7801  		}
  7802  
  7803  	case len(oldService.Spec.IPFamilies) > len(service.Spec.IPFamilies):
  7804  		// something has been released (downgraded)
  7805  
  7806  		// test if primary ipfamily has been released
  7807  		if len(service.Spec.ClusterIPs) == 0 {
  7808  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.IPFamilies, "primary ipFamily can not be unset"))
  7809  		}
  7810  
  7811  		// test if primary ipFamily has changed
  7812  		if len(service.Spec.IPFamilies) > 0 &&
  7813  			service.Spec.IPFamilies[0] != oldService.Spec.IPFamilies[0] {
  7814  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7815  		}
  7816  
  7817  		// test if secondary IPFamily has been released. has this service been downgraded correctly?
  7818  		// user *must* set IPFamilyPolicy == SingleStack
  7819  		if len(service.Spec.IPFamilies) == 1 {
  7820  			if service.Spec.IPFamilyPolicy == nil || *(service.Spec.IPFamilyPolicy) != core.IPFamilyPolicySingleStack {
  7821  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilyPolicy"), service.Spec.IPFamilyPolicy, "must be set to 'SingleStack' when releasing the secondary ipFamily"))
  7822  			}
  7823  		}
  7824  	case len(oldService.Spec.IPFamilies) < len(service.Spec.IPFamilies):
  7825  		// something has been added (upgraded)
  7826  
  7827  		// test if primary ipFamily has changed
  7828  		if len(oldService.Spec.IPFamilies) > 0 &&
  7829  			len(service.Spec.IPFamilies) > 0 &&
  7830  			service.Spec.IPFamilies[0] != oldService.Spec.IPFamilies[0] {
  7831  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7832  		}
  7833  		// we don't check for Policy == RequireDualStack here since, Validation/Creation func takes care of it
  7834  	}
  7835  	return allErrs
  7836  }
  7837  
  7838  func isHeadlessService(service *core.Service) bool {
  7839  	return service != nil &&
  7840  		len(service.Spec.ClusterIPs) == 1 &&
  7841  		service.Spec.ClusterIPs[0] == core.ClusterIPNone
  7842  }
  7843  
  7844  // validateLoadBalancerClassField validation for loadBalancerClass
  7845  func validateLoadBalancerClassField(oldService, service *core.Service) field.ErrorList {
  7846  	allErrs := make(field.ErrorList, 0)
  7847  	if oldService != nil {
  7848  		// validate update op
  7849  		if isTypeLoadBalancer(oldService) && isTypeLoadBalancer(service) {
  7850  			// old and new are both LoadBalancer
  7851  			if !sameLoadBalancerClass(oldService, service) {
  7852  				// can't change loadBalancerClass
  7853  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "loadBalancerClass"), service.Spec.LoadBalancerClass, "may not change once set"))
  7854  			}
  7855  		}
  7856  	}
  7857  
  7858  	if isTypeLoadBalancer(service) {
  7859  		// check LoadBalancerClass format
  7860  		if service.Spec.LoadBalancerClass != nil {
  7861  			allErrs = append(allErrs, ValidateQualifiedName(*service.Spec.LoadBalancerClass, field.NewPath("spec", "loadBalancerClass"))...)
  7862  		}
  7863  	} else {
  7864  		// check if LoadBalancerClass set for non LoadBalancer type of service
  7865  		if service.Spec.LoadBalancerClass != nil {
  7866  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "loadBalancerClass"), "may only be used when `type` is 'LoadBalancer'"))
  7867  		}
  7868  	}
  7869  	return allErrs
  7870  }
  7871  
  7872  // isTypeLoadBalancer tests service type is loadBalancer or not
  7873  func isTypeLoadBalancer(service *core.Service) bool {
  7874  	return service.Spec.Type == core.ServiceTypeLoadBalancer
  7875  }
  7876  
  7877  // sameLoadBalancerClass check two services have the same loadBalancerClass or not
  7878  func sameLoadBalancerClass(oldService, service *core.Service) bool {
  7879  	if oldService.Spec.LoadBalancerClass == nil && service.Spec.LoadBalancerClass == nil {
  7880  		return true
  7881  	}
  7882  	if oldService.Spec.LoadBalancerClass == nil || service.Spec.LoadBalancerClass == nil {
  7883  		return false
  7884  	}
  7885  	return *oldService.Spec.LoadBalancerClass == *service.Spec.LoadBalancerClass
  7886  }
  7887  
  7888  func ValidatePodAffinityTermSelector(podAffinityTerm core.PodAffinityTerm, allowInvalidLabelValueInSelector bool, fldPath *field.Path) field.ErrorList {
  7889  	var allErrs field.ErrorList
  7890  	labelSelectorValidationOptions := unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: allowInvalidLabelValueInSelector}
  7891  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.LabelSelector, labelSelectorValidationOptions, fldPath.Child("labelSelector"))...)
  7892  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.NamespaceSelector, labelSelectorValidationOptions, fldPath.Child("namespaceSelector"))...)
  7893  	return allErrs
  7894  }
  7895  
  7896  var betaToGALabel = map[string]string{
  7897  	v1.LabelFailureDomainBetaZone:   v1.LabelTopologyZone,
  7898  	v1.LabelFailureDomainBetaRegion: v1.LabelTopologyRegion,
  7899  	kubeletapis.LabelOS:             v1.LabelOSStable,
  7900  	kubeletapis.LabelArch:           v1.LabelArchStable,
  7901  	v1.LabelInstanceType:            v1.LabelInstanceTypeStable,
  7902  }
  7903  
  7904  var (
  7905  	maskNodeSelectorLabelChangeEqualities     conversion.Equalities
  7906  	initMaskNodeSelectorLabelChangeEqualities sync.Once
  7907  )
  7908  
  7909  func getMaskNodeSelectorLabelChangeEqualities() conversion.Equalities {
  7910  	initMaskNodeSelectorLabelChangeEqualities.Do(func() {
  7911  		var eqs = apiequality.Semantic.Copy()
  7912  		err := eqs.AddFunc(
  7913  			func(newReq, oldReq core.NodeSelectorRequirement) bool {
  7914  				// allow newReq to change to a GA key
  7915  				if oldReq.Key != newReq.Key && betaToGALabel[oldReq.Key] == newReq.Key {
  7916  					oldReq.Key = newReq.Key // +k8s:verify-mutation:reason=clone
  7917  				}
  7918  				return apiequality.Semantic.DeepEqual(newReq, oldReq)
  7919  			},
  7920  		)
  7921  		if err != nil {
  7922  			panic(fmt.Errorf("failed to instantiate semantic equalities: %w", err))
  7923  		}
  7924  		maskNodeSelectorLabelChangeEqualities = eqs
  7925  	})
  7926  	return maskNodeSelectorLabelChangeEqualities
  7927  }
  7928  
  7929  func validatePvNodeAffinity(newPvNodeAffinity, oldPvNodeAffinity *core.VolumeNodeAffinity, fldPath *field.Path) field.ErrorList {
  7930  	var allErrs field.ErrorList
  7931  	if !getMaskNodeSelectorLabelChangeEqualities().DeepEqual(newPvNodeAffinity, oldPvNodeAffinity) {
  7932  		allErrs = append(allErrs, field.Invalid(fldPath, newPvNodeAffinity, fieldImmutableErrorMsg+", except for updating from beta label to GA"))
  7933  	}
  7934  	return allErrs
  7935  }
  7936  
  7937  func validateNodeSelectorMutation(fldPath *field.Path, newNodeSelector, oldNodeSelector map[string]string) field.ErrorList {
  7938  	var allErrs field.ErrorList
  7939  
  7940  	// Validate no existing node selectors were deleted or mutated.
  7941  	for k, v1 := range oldNodeSelector {
  7942  		if v2, ok := newNodeSelector[k]; !ok || v1 != v2 {
  7943  			allErrs = append(allErrs, field.Invalid(fldPath, newNodeSelector, "only additions to spec.nodeSelector are allowed (no mutations or deletions)"))
  7944  			return allErrs
  7945  		}
  7946  	}
  7947  	return allErrs
  7948  }
  7949  
  7950  func validateNodeAffinityMutation(nodeAffinityPath *field.Path, newNodeAffinity, oldNodeAffinity *core.NodeAffinity) field.ErrorList {
  7951  	var allErrs field.ErrorList
  7952  	// If old node affinity was nil, anything can be set.
  7953  	if oldNodeAffinity == nil || oldNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
  7954  		return allErrs
  7955  	}
  7956  
  7957  	oldTerms := oldNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
  7958  	var newTerms []core.NodeSelectorTerm
  7959  	if newNodeAffinity != nil && newNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  7960  		newTerms = newNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
  7961  	}
  7962  
  7963  	// If there are no old terms, we can set the new terms to anything.
  7964  	// If there are old terms, we cannot add any new ones.
  7965  	if len(oldTerms) > 0 && len(oldTerms) != len(newTerms) {
  7966  		return append(allErrs, field.Invalid(nodeAffinityPath.Child("requiredDuringSchedulingIgnoredDuringExecution").Child("nodeSelectorTerms"), newTerms, "no additions/deletions to non-empty NodeSelectorTerms list are allowed"))
  7967  	}
  7968  
  7969  	// For requiredDuringSchedulingIgnoredDuringExecution, if old NodeSelectorTerms
  7970  	// was empty, anything can be set. If non-empty, only additions of NodeSelectorRequirements
  7971  	// to matchExpressions or fieldExpressions are allowed.
  7972  	for i := range oldTerms {
  7973  		if !validateNodeSelectorTermHasOnlyAdditions(newTerms[i], oldTerms[i]) {
  7974  			allErrs = append(allErrs, field.Invalid(nodeAffinityPath.Child("requiredDuringSchedulingIgnoredDuringExecution").Child("nodeSelectorTerms").Index(i), newTerms[i], "only additions are allowed (no mutations or deletions)"))
  7975  		}
  7976  	}
  7977  	return allErrs
  7978  }
  7979  
  7980  func validateNodeSelectorTermHasOnlyAdditions(newTerm, oldTerm core.NodeSelectorTerm) bool {
  7981  	if len(oldTerm.MatchExpressions) == 0 && len(oldTerm.MatchFields) == 0 {
  7982  		if len(newTerm.MatchExpressions) > 0 || len(newTerm.MatchFields) > 0 {
  7983  			return false
  7984  		}
  7985  	}
  7986  
  7987  	// Validate MatchExpressions only has additions (no deletions or mutations)
  7988  	if l := len(oldTerm.MatchExpressions); l > 0 {
  7989  		if len(newTerm.MatchExpressions) < l {
  7990  			return false
  7991  		}
  7992  		if !apiequality.Semantic.DeepEqual(newTerm.MatchExpressions[:l], oldTerm.MatchExpressions) {
  7993  			return false
  7994  		}
  7995  	}
  7996  	// Validate MatchFields only has additions (no deletions or mutations)
  7997  	if l := len(oldTerm.MatchFields); l > 0 {
  7998  		if len(newTerm.MatchFields) < l {
  7999  			return false
  8000  		}
  8001  		if !apiequality.Semantic.DeepEqual(newTerm.MatchFields[:l], oldTerm.MatchFields) {
  8002  			return false
  8003  		}
  8004  	}
  8005  	return true
  8006  }