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