...

Source file src/k8s.io/kubernetes/pkg/apis/core/validation/validation.go

Documentation: k8s.io/kubernetes/pkg/apis/core/validation

     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, oldTemplate *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  		// get rid of apivalidation.ValidateReadOnlyPersistentDisks,stop passing oldTemplate to this function
  5928  		var oldVols []core.Volume
  5929  		if oldTemplate != nil {
  5930  			oldVols = oldTemplate.Spec.Volumes // +k8s:verify-mutation:reason=clone
  5931  		}
  5932  		if replicas > 1 {
  5933  			allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(template.Spec.Volumes, oldVols, fldPath.Child("spec", "volumes"))...)
  5934  		}
  5935  		// RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
  5936  		if template.Spec.RestartPolicy != core.RestartPolicyAlways {
  5937  			allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []core.RestartPolicy{core.RestartPolicyAlways}))
  5938  		}
  5939  		if template.Spec.ActiveDeadlineSeconds != nil {
  5940  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "activeDeadlineSeconds"), "activeDeadlineSeconds in ReplicationController is not Supported"))
  5941  		}
  5942  	}
  5943  	return allErrs
  5944  }
  5945  
  5946  // ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
  5947  func ValidateReplicationControllerSpec(spec, oldSpec *core.ReplicationControllerSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  5948  	allErrs := field.ErrorList{}
  5949  	allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
  5950  	allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...)
  5951  	allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
  5952  	// oldSpec is not empty, pass oldSpec.template.
  5953  	var oldTemplate *core.PodTemplateSpec
  5954  	if oldSpec != nil {
  5955  		oldTemplate = oldSpec.Template // +k8s:verify-mutation:reason=clone
  5956  	}
  5957  	allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, oldTemplate, spec.Selector, spec.Replicas, fldPath.Child("template"), opts)...)
  5958  	return allErrs
  5959  }
  5960  
  5961  // ValidatePodTemplateSpec validates the spec of a pod template
  5962  func ValidatePodTemplateSpec(spec *core.PodTemplateSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  5963  	allErrs := field.ErrorList{}
  5964  	allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.Labels, fldPath.Child("labels"))...)
  5965  	allErrs = append(allErrs, ValidateAnnotations(spec.Annotations, fldPath.Child("annotations"))...)
  5966  	allErrs = append(allErrs, ValidatePodSpecificAnnotations(spec.Annotations, &spec.Spec, fldPath.Child("annotations"), opts)...)
  5967  	allErrs = append(allErrs, ValidatePodSpec(&spec.Spec, nil, fldPath.Child("spec"), opts)...)
  5968  	allErrs = append(allErrs, validateSeccompAnnotationsAndFields(spec.ObjectMeta, &spec.Spec, fldPath.Child("spec"))...)
  5969  	allErrs = append(allErrs, validateAppArmorAnnotationsAndFieldsMatchOnCreate(spec.ObjectMeta, &spec.Spec, fldPath.Child("spec"))...)
  5970  
  5971  	if len(spec.Spec.EphemeralContainers) > 0 {
  5972  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("spec", "ephemeralContainers"), "ephemeral containers not allowed in pod template"))
  5973  	}
  5974  
  5975  	return allErrs
  5976  }
  5977  
  5978  // ValidateReadOnlyPersistentDisks stick this AFTER the short-circuit checks
  5979  func ValidateReadOnlyPersistentDisks(volumes, oldVolumes []core.Volume, fldPath *field.Path) field.ErrorList {
  5980  	allErrs := field.ErrorList{}
  5981  
  5982  	if utilfeature.DefaultFeatureGate.Enabled(features.SkipReadOnlyValidationGCE) {
  5983  		return field.ErrorList{}
  5984  	}
  5985  
  5986  	isWriteablePD := func(vol *core.Volume) bool {
  5987  		return vol.GCEPersistentDisk != nil && !vol.GCEPersistentDisk.ReadOnly
  5988  	}
  5989  
  5990  	for i := range oldVolumes {
  5991  		if isWriteablePD(&oldVolumes[i]) {
  5992  			return field.ErrorList{}
  5993  		}
  5994  	}
  5995  
  5996  	for i := range volumes {
  5997  		idxPath := fldPath.Index(i)
  5998  		if isWriteablePD(&volumes[i]) {
  5999  			allErrs = append(allErrs, field.Invalid(idxPath.Child("gcePersistentDisk", "readOnly"), false, "must be true for replicated pods > 1; GCE PD can only be mounted on multiple machines if it is read-only"))
  6000  		}
  6001  	}
  6002  	return allErrs
  6003  }
  6004  
  6005  // ValidateTaintsInNodeAnnotations tests that the serialized taints in Node.Annotations has valid data
  6006  func ValidateTaintsInNodeAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  6007  	allErrs := field.ErrorList{}
  6008  
  6009  	taints, err := helper.GetTaintsFromNodeAnnotations(annotations)
  6010  	if err != nil {
  6011  		allErrs = append(allErrs, field.Invalid(fldPath, core.TaintsAnnotationKey, err.Error()))
  6012  		return allErrs
  6013  	}
  6014  
  6015  	if len(taints) > 0 {
  6016  		allErrs = append(allErrs, validateNodeTaints(taints, fldPath.Child(core.TaintsAnnotationKey))...)
  6017  	}
  6018  
  6019  	return allErrs
  6020  }
  6021  
  6022  // validateNodeTaints tests if given taints have valid data.
  6023  func validateNodeTaints(taints []core.Taint, fldPath *field.Path) field.ErrorList {
  6024  	allErrors := field.ErrorList{}
  6025  
  6026  	uniqueTaints := map[core.TaintEffect]sets.Set[string]{}
  6027  
  6028  	for i, currTaint := range taints {
  6029  		idxPath := fldPath.Index(i)
  6030  		// validate the taint key
  6031  		allErrors = append(allErrors, unversionedvalidation.ValidateLabelName(currTaint.Key, idxPath.Child("key"))...)
  6032  		// validate the taint value
  6033  		if errs := validation.IsValidLabelValue(currTaint.Value); len(errs) != 0 {
  6034  			allErrors = append(allErrors, field.Invalid(idxPath.Child("value"), currTaint.Value, strings.Join(errs, ";")))
  6035  		}
  6036  		// validate the taint effect
  6037  		allErrors = append(allErrors, validateTaintEffect(&currTaint.Effect, false, idxPath.Child("effect"))...)
  6038  
  6039  		// validate if taint is unique by <key, effect>
  6040  		if len(uniqueTaints[currTaint.Effect]) > 0 && uniqueTaints[currTaint.Effect].Has(currTaint.Key) {
  6041  			duplicatedError := field.Duplicate(idxPath, currTaint)
  6042  			duplicatedError.Detail = "taints must be unique by key and effect pair"
  6043  			allErrors = append(allErrors, duplicatedError)
  6044  			continue
  6045  		}
  6046  
  6047  		// add taint to existingTaints for uniqueness check
  6048  		if len(uniqueTaints[currTaint.Effect]) == 0 {
  6049  			uniqueTaints[currTaint.Effect] = sets.Set[string]{}
  6050  		}
  6051  		uniqueTaints[currTaint.Effect].Insert(currTaint.Key)
  6052  	}
  6053  	return allErrors
  6054  }
  6055  
  6056  func ValidateNodeSpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  6057  	allErrs := field.ErrorList{}
  6058  
  6059  	if annotations[core.TaintsAnnotationKey] != "" {
  6060  		allErrs = append(allErrs, ValidateTaintsInNodeAnnotations(annotations, fldPath)...)
  6061  	}
  6062  
  6063  	if annotations[core.PreferAvoidPodsAnnotationKey] != "" {
  6064  		allErrs = append(allErrs, ValidateAvoidPodsInNodeAnnotations(annotations, fldPath)...)
  6065  	}
  6066  	return allErrs
  6067  }
  6068  
  6069  // ValidateNode tests if required fields in the node are set.
  6070  func ValidateNode(node *core.Node) field.ErrorList {
  6071  	fldPath := field.NewPath("metadata")
  6072  	allErrs := ValidateObjectMeta(&node.ObjectMeta, false, ValidateNodeName, fldPath)
  6073  	allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
  6074  	if len(node.Spec.Taints) > 0 {
  6075  		allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...)
  6076  	}
  6077  
  6078  	// Only validate spec.
  6079  	// All status fields are optional and can be updated later.
  6080  	// That said, if specified, we need to ensure they are valid.
  6081  	allErrs = append(allErrs, ValidateNodeResources(node)...)
  6082  
  6083  	// validate PodCIDRS only if we need to
  6084  	if len(node.Spec.PodCIDRs) > 0 {
  6085  		podCIDRsField := field.NewPath("spec", "podCIDRs")
  6086  
  6087  		// all PodCIDRs should be valid ones
  6088  		for idx, value := range node.Spec.PodCIDRs {
  6089  			allErrs = append(allErrs, validation.IsValidCIDR(podCIDRsField.Index(idx), value)...)
  6090  		}
  6091  
  6092  		// if more than PodCIDR then
  6093  		// - validate for dual stack
  6094  		// - validate for duplication
  6095  		if len(node.Spec.PodCIDRs) > 1 {
  6096  			dualStack, err := netutils.IsDualStackCIDRStrings(node.Spec.PodCIDRs)
  6097  			if err != nil {
  6098  				allErrs = append(allErrs, field.InternalError(podCIDRsField, fmt.Errorf("invalid PodCIDRs. failed to check with dual stack with error:%v", err)))
  6099  			}
  6100  			if !dualStack || len(node.Spec.PodCIDRs) > 2 {
  6101  				allErrs = append(allErrs, field.Invalid(podCIDRsField, node.Spec.PodCIDRs, "may specify no more than one CIDR for each IP family"))
  6102  			}
  6103  
  6104  			// PodCIDRs must not contain duplicates
  6105  			seen := sets.Set[string]{}
  6106  			for i, value := range node.Spec.PodCIDRs {
  6107  				if seen.Has(value) {
  6108  					allErrs = append(allErrs, field.Duplicate(podCIDRsField.Index(i), value))
  6109  				}
  6110  				seen.Insert(value)
  6111  			}
  6112  		}
  6113  	}
  6114  
  6115  	return allErrs
  6116  }
  6117  
  6118  // ValidateNodeResources is used to make sure a node has valid capacity and allocatable values.
  6119  func ValidateNodeResources(node *core.Node) field.ErrorList {
  6120  	allErrs := field.ErrorList{}
  6121  
  6122  	// Validate resource quantities in capacity.
  6123  	for k, v := range node.Status.Capacity {
  6124  		resPath := field.NewPath("status", "capacity", string(k))
  6125  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6126  	}
  6127  
  6128  	// Validate resource quantities in allocatable.
  6129  	for k, v := range node.Status.Allocatable {
  6130  		resPath := field.NewPath("status", "allocatable", string(k))
  6131  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6132  	}
  6133  	return allErrs
  6134  }
  6135  
  6136  // ValidateNodeUpdate tests to make sure a node update can be applied.  Modifies oldNode.
  6137  func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
  6138  	fldPath := field.NewPath("metadata")
  6139  	allErrs := ValidateObjectMetaUpdate(&node.ObjectMeta, &oldNode.ObjectMeta, fldPath)
  6140  	allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...)
  6141  
  6142  	// TODO: Enable the code once we have better core object.status update model. Currently,
  6143  	// anyone can update node status.
  6144  	// if !apiequality.Semantic.DeepEqual(node.Status, core.NodeStatus{}) {
  6145  	// 	allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty"))
  6146  	// }
  6147  
  6148  	allErrs = append(allErrs, ValidateNodeResources(node)...)
  6149  
  6150  	// Validate no duplicate addresses in node status.
  6151  	addresses := make(map[core.NodeAddress]bool)
  6152  	for i, address := range node.Status.Addresses {
  6153  		if _, ok := addresses[address]; ok {
  6154  			allErrs = append(allErrs, field.Duplicate(field.NewPath("status", "addresses").Index(i), address))
  6155  		}
  6156  		addresses[address] = true
  6157  	}
  6158  
  6159  	// Allow the controller manager to assign a CIDR to a node if it doesn't have one.
  6160  	if len(oldNode.Spec.PodCIDRs) > 0 {
  6161  		// compare the entire slice
  6162  		if len(oldNode.Spec.PodCIDRs) != len(node.Spec.PodCIDRs) {
  6163  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid"))
  6164  		} else {
  6165  			for idx, value := range oldNode.Spec.PodCIDRs {
  6166  				if value != node.Spec.PodCIDRs[idx] {
  6167  					allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid"))
  6168  				}
  6169  			}
  6170  		}
  6171  	}
  6172  
  6173  	// Allow controller manager updating provider ID when not set
  6174  	if len(oldNode.Spec.ProviderID) > 0 && oldNode.Spec.ProviderID != node.Spec.ProviderID {
  6175  		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "providerID"), "node updates may not change providerID except from \"\" to valid"))
  6176  	}
  6177  
  6178  	if node.Spec.ConfigSource != nil {
  6179  		allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
  6180  	}
  6181  	if node.Status.Config != nil {
  6182  		allErrs = append(allErrs, validateNodeConfigStatus(node.Status.Config, field.NewPath("status", "config"))...)
  6183  	}
  6184  
  6185  	// update taints
  6186  	if len(node.Spec.Taints) > 0 {
  6187  		allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...)
  6188  	}
  6189  
  6190  	if node.Spec.DoNotUseExternalID != oldNode.Spec.DoNotUseExternalID {
  6191  		allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "externalID"), "may not be updated"))
  6192  	}
  6193  
  6194  	// status and metadata are allowed change (barring restrictions above), so separately test spec field.
  6195  	// spec only has a few fields, so check the ones we don't allow changing
  6196  	//  1. PodCIDRs - immutable after first set - checked above
  6197  	//  2. ProviderID - immutable after first set - checked above
  6198  	//  3. Unschedulable - allowed to change
  6199  	//  4. Taints - allowed to change
  6200  	//  5. ConfigSource - allowed to change (and checked above)
  6201  	//  6. DoNotUseExternalID - immutable - checked above
  6202  
  6203  	return allErrs
  6204  }
  6205  
  6206  // validation specific to Node.Spec.ConfigSource
  6207  // The field ConfigSource is deprecated and will not be used. The validation is kept in place
  6208  // for the backward compatibility
  6209  func validateNodeConfigSourceSpec(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
  6210  	allErrs := field.ErrorList{}
  6211  	count := int(0)
  6212  	if source.ConfigMap != nil {
  6213  		count++
  6214  		allErrs = append(allErrs, validateConfigMapNodeConfigSourceSpec(source.ConfigMap, fldPath.Child("configMap"))...)
  6215  	}
  6216  	// add more subfields here in the future as they are added to NodeConfigSource
  6217  
  6218  	// exactly one reference subfield must be non-nil
  6219  	if count != 1 {
  6220  		allErrs = append(allErrs, field.Invalid(fldPath, source, "exactly one reference subfield must be non-nil"))
  6221  	}
  6222  	return allErrs
  6223  }
  6224  
  6225  // validation specific to Node.Spec.ConfigSource.ConfigMap
  6226  // The field ConfigSource is deprecated and will not be used. The validation is kept in place
  6227  // for the backward compatibility
  6228  func validateConfigMapNodeConfigSourceSpec(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  6229  	allErrs := field.ErrorList{}
  6230  	// uid and resourceVersion must not be set in spec
  6231  	if string(source.UID) != "" {
  6232  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("uid"), "uid must not be set in spec"))
  6233  	}
  6234  	if source.ResourceVersion != "" {
  6235  		allErrs = append(allErrs, field.Forbidden(fldPath.Child("resourceVersion"), "resourceVersion must not be set in spec"))
  6236  	}
  6237  	return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
  6238  }
  6239  
  6240  // validation specififc to Node.Status.Config
  6241  func validateNodeConfigStatus(status *core.NodeConfigStatus, fldPath *field.Path) field.ErrorList {
  6242  	allErrs := field.ErrorList{}
  6243  	if status.Assigned != nil {
  6244  		allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Assigned, fldPath.Child("assigned"))...)
  6245  	}
  6246  	if status.Active != nil {
  6247  		allErrs = append(allErrs, validateNodeConfigSourceStatus(status.Active, fldPath.Child("active"))...)
  6248  	}
  6249  	if status.LastKnownGood != nil {
  6250  		allErrs = append(allErrs, validateNodeConfigSourceStatus(status.LastKnownGood, fldPath.Child("lastKnownGood"))...)
  6251  	}
  6252  	return allErrs
  6253  }
  6254  
  6255  // validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood)
  6256  func validateNodeConfigSourceStatus(source *core.NodeConfigSource, fldPath *field.Path) field.ErrorList {
  6257  	allErrs := field.ErrorList{}
  6258  	count := int(0)
  6259  	if source.ConfigMap != nil {
  6260  		count++
  6261  		allErrs = append(allErrs, validateConfigMapNodeConfigSourceStatus(source.ConfigMap, fldPath.Child("configMap"))...)
  6262  	}
  6263  	// add more subfields here in the future as they are added to NodeConfigSource
  6264  
  6265  	// exactly one reference subfield must be non-nil
  6266  	if count != 1 {
  6267  		allErrs = append(allErrs, field.Invalid(fldPath, source, "exactly one reference subfield must be non-nil"))
  6268  	}
  6269  	return allErrs
  6270  }
  6271  
  6272  // validation specific to Node.Status.Config.(Active|Assigned|LastKnownGood).ConfigMap
  6273  func validateConfigMapNodeConfigSourceStatus(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  6274  	allErrs := field.ErrorList{}
  6275  	// uid and resourceVersion must be set in status
  6276  	if string(source.UID) == "" {
  6277  		allErrs = append(allErrs, field.Required(fldPath.Child("uid"), "uid must be set in status"))
  6278  	}
  6279  	if source.ResourceVersion == "" {
  6280  		allErrs = append(allErrs, field.Required(fldPath.Child("resourceVersion"), "resourceVersion must be set in status"))
  6281  	}
  6282  	return append(allErrs, validateConfigMapNodeConfigSource(source, fldPath)...)
  6283  }
  6284  
  6285  // common validation
  6286  func validateConfigMapNodeConfigSource(source *core.ConfigMapNodeConfigSource, fldPath *field.Path) field.ErrorList {
  6287  	allErrs := field.ErrorList{}
  6288  	// validate target configmap namespace
  6289  	if source.Namespace == "" {
  6290  		allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "namespace must be set"))
  6291  	} else {
  6292  		for _, msg := range ValidateNameFunc(ValidateNamespaceName)(source.Namespace, false) {
  6293  			allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), source.Namespace, msg))
  6294  		}
  6295  	}
  6296  	// validate target configmap name
  6297  	if source.Name == "" {
  6298  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name must be set"))
  6299  	} else {
  6300  		for _, msg := range ValidateNameFunc(ValidateConfigMapName)(source.Name, false) {
  6301  			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), source.Name, msg))
  6302  		}
  6303  	}
  6304  	// validate kubeletConfigKey against rules for configMap key names
  6305  	if source.KubeletConfigKey == "" {
  6306  		allErrs = append(allErrs, field.Required(fldPath.Child("kubeletConfigKey"), "kubeletConfigKey must be set"))
  6307  	} else {
  6308  		for _, msg := range validation.IsConfigMapKey(source.KubeletConfigKey) {
  6309  			allErrs = append(allErrs, field.Invalid(fldPath.Child("kubeletConfigKey"), source.KubeletConfigKey, msg))
  6310  		}
  6311  	}
  6312  	return allErrs
  6313  }
  6314  
  6315  // Validate compute resource typename.
  6316  // Refer to docs/design/resources.md for more details.
  6317  func validateResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6318  	allErrs := field.ErrorList{}
  6319  	for _, msg := range validation.IsQualifiedName(string(value)) {
  6320  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  6321  	}
  6322  	if len(allErrs) != 0 {
  6323  		return allErrs
  6324  	}
  6325  
  6326  	if len(strings.Split(string(value), "/")) == 1 {
  6327  		if !helper.IsStandardResourceName(value) {
  6328  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource type or fully qualified"))
  6329  		}
  6330  	}
  6331  
  6332  	return allErrs
  6333  }
  6334  
  6335  // Validate container resource name
  6336  // Refer to docs/design/resources.md for more details.
  6337  func validateContainerResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6338  	allErrs := validateResourceName(value, fldPath)
  6339  
  6340  	if len(strings.Split(string(value), "/")) == 1 {
  6341  		if !helper.IsStandardContainerResourceName(value) {
  6342  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource for containers"))
  6343  		}
  6344  	} else if !helper.IsNativeResource(value) {
  6345  		if !helper.IsExtendedResourceName(value) {
  6346  			return append(allErrs, field.Invalid(fldPath, value, "doesn't follow extended resource name standard"))
  6347  		}
  6348  	}
  6349  	return allErrs
  6350  }
  6351  
  6352  // Validate resource names that can go in a resource quota
  6353  // Refer to docs/design/resources.md for more details.
  6354  func ValidateResourceQuotaResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6355  	allErrs := validateResourceName(value, fldPath)
  6356  
  6357  	if len(strings.Split(string(value), "/")) == 1 {
  6358  		if !helper.IsStandardQuotaResourceName(value) {
  6359  			return append(allErrs, field.Invalid(fldPath, value, isInvalidQuotaResource))
  6360  		}
  6361  	}
  6362  	return allErrs
  6363  }
  6364  
  6365  // Validate limit range types
  6366  func validateLimitRangeTypeName(value core.LimitType, fldPath *field.Path) field.ErrorList {
  6367  	allErrs := field.ErrorList{}
  6368  	for _, msg := range validation.IsQualifiedName(string(value)) {
  6369  		allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
  6370  	}
  6371  	if len(allErrs) != 0 {
  6372  		return allErrs
  6373  	}
  6374  
  6375  	if len(strings.Split(string(value), "/")) == 1 {
  6376  		if !helper.IsStandardLimitRangeType(value) {
  6377  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard limit type or fully qualified"))
  6378  		}
  6379  	}
  6380  
  6381  	return allErrs
  6382  }
  6383  
  6384  // Validate limit range resource name
  6385  // limit types (other than Pod/Container) could contain storage not just cpu or memory
  6386  func validateLimitRangeResourceName(limitType core.LimitType, value core.ResourceName, fldPath *field.Path) field.ErrorList {
  6387  	switch limitType {
  6388  	case core.LimitTypePod, core.LimitTypeContainer:
  6389  		return validateContainerResourceName(value, fldPath)
  6390  	default:
  6391  		return validateResourceName(value, fldPath)
  6392  	}
  6393  }
  6394  
  6395  // ValidateLimitRange tests if required fields in the LimitRange are set.
  6396  func ValidateLimitRange(limitRange *core.LimitRange) field.ErrorList {
  6397  	allErrs := ValidateObjectMeta(&limitRange.ObjectMeta, true, ValidateLimitRangeName, field.NewPath("metadata"))
  6398  
  6399  	// ensure resource names are properly qualified per docs/design/resources.md
  6400  	limitTypeSet := map[core.LimitType]bool{}
  6401  	fldPath := field.NewPath("spec", "limits")
  6402  	for i := range limitRange.Spec.Limits {
  6403  		idxPath := fldPath.Index(i)
  6404  		limit := &limitRange.Spec.Limits[i]
  6405  		allErrs = append(allErrs, validateLimitRangeTypeName(limit.Type, idxPath.Child("type"))...)
  6406  
  6407  		_, found := limitTypeSet[limit.Type]
  6408  		if found {
  6409  			allErrs = append(allErrs, field.Duplicate(idxPath.Child("type"), limit.Type))
  6410  		}
  6411  		limitTypeSet[limit.Type] = true
  6412  
  6413  		keys := sets.Set[string]{}
  6414  		min := map[string]resource.Quantity{}
  6415  		max := map[string]resource.Quantity{}
  6416  		defaults := map[string]resource.Quantity{}
  6417  		defaultRequests := map[string]resource.Quantity{}
  6418  		maxLimitRequestRatios := map[string]resource.Quantity{}
  6419  
  6420  		for k, q := range limit.Max {
  6421  			allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("max").Key(string(k)))...)
  6422  			keys.Insert(string(k))
  6423  			max[string(k)] = q
  6424  		}
  6425  		for k, q := range limit.Min {
  6426  			allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("min").Key(string(k)))...)
  6427  			keys.Insert(string(k))
  6428  			min[string(k)] = q
  6429  		}
  6430  
  6431  		if limit.Type == core.LimitTypePod {
  6432  			if len(limit.Default) > 0 {
  6433  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("default"), "may not be specified when `type` is 'Pod'"))
  6434  			}
  6435  			if len(limit.DefaultRequest) > 0 {
  6436  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("defaultRequest"), "may not be specified when `type` is 'Pod'"))
  6437  			}
  6438  		} else {
  6439  			for k, q := range limit.Default {
  6440  				allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("default").Key(string(k)))...)
  6441  				keys.Insert(string(k))
  6442  				defaults[string(k)] = q
  6443  			}
  6444  			for k, q := range limit.DefaultRequest {
  6445  				allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("defaultRequest").Key(string(k)))...)
  6446  				keys.Insert(string(k))
  6447  				defaultRequests[string(k)] = q
  6448  			}
  6449  		}
  6450  
  6451  		if limit.Type == core.LimitTypePersistentVolumeClaim {
  6452  			_, minQuantityFound := limit.Min[core.ResourceStorage]
  6453  			_, maxQuantityFound := limit.Max[core.ResourceStorage]
  6454  			if !minQuantityFound && !maxQuantityFound {
  6455  				allErrs = append(allErrs, field.Required(idxPath.Child("limits"), "either minimum or maximum storage value is required, but neither was provided"))
  6456  			}
  6457  		}
  6458  
  6459  		for k, q := range limit.MaxLimitRequestRatio {
  6460  			allErrs = append(allErrs, validateLimitRangeResourceName(limit.Type, k, idxPath.Child("maxLimitRequestRatio").Key(string(k)))...)
  6461  			keys.Insert(string(k))
  6462  			maxLimitRequestRatios[string(k)] = q
  6463  		}
  6464  
  6465  		for k := range keys {
  6466  			minQuantity, minQuantityFound := min[k]
  6467  			maxQuantity, maxQuantityFound := max[k]
  6468  			defaultQuantity, defaultQuantityFound := defaults[k]
  6469  			defaultRequestQuantity, defaultRequestQuantityFound := defaultRequests[k]
  6470  			maxRatio, maxRatioFound := maxLimitRequestRatios[k]
  6471  
  6472  			if minQuantityFound && maxQuantityFound && minQuantity.Cmp(maxQuantity) > 0 {
  6473  				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())))
  6474  			}
  6475  
  6476  			if defaultRequestQuantityFound && minQuantityFound && minQuantity.Cmp(defaultRequestQuantity) > 0 {
  6477  				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())))
  6478  			}
  6479  
  6480  			if defaultRequestQuantityFound && maxQuantityFound && defaultRequestQuantity.Cmp(maxQuantity) > 0 {
  6481  				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())))
  6482  			}
  6483  
  6484  			if defaultRequestQuantityFound && defaultQuantityFound && defaultRequestQuantity.Cmp(defaultQuantity) > 0 {
  6485  				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())))
  6486  			}
  6487  
  6488  			if defaultQuantityFound && minQuantityFound && minQuantity.Cmp(defaultQuantity) > 0 {
  6489  				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())))
  6490  			}
  6491  
  6492  			if defaultQuantityFound && maxQuantityFound && defaultQuantity.Cmp(maxQuantity) > 0 {
  6493  				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())))
  6494  			}
  6495  			if maxRatioFound && maxRatio.Cmp(*resource.NewQuantity(1, resource.DecimalSI)) < 0 {
  6496  				allErrs = append(allErrs, field.Invalid(idxPath.Child("maxLimitRequestRatio").Key(string(k)), maxRatio, fmt.Sprintf("ratio %s is less than 1", maxRatio.String())))
  6497  			}
  6498  			if maxRatioFound && minQuantityFound && maxQuantityFound {
  6499  				maxRatioValue := float64(maxRatio.Value())
  6500  				minQuantityValue := minQuantity.Value()
  6501  				maxQuantityValue := maxQuantity.Value()
  6502  				if maxRatio.Value() < resource.MaxMilliValue && minQuantityValue < resource.MaxMilliValue && maxQuantityValue < resource.MaxMilliValue {
  6503  					maxRatioValue = float64(maxRatio.MilliValue()) / 1000
  6504  					minQuantityValue = minQuantity.MilliValue()
  6505  					maxQuantityValue = maxQuantity.MilliValue()
  6506  				}
  6507  				maxRatioLimit := float64(maxQuantityValue) / float64(minQuantityValue)
  6508  				if maxRatioValue > maxRatioLimit {
  6509  					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)))
  6510  				}
  6511  			}
  6512  
  6513  			// for GPU, hugepages and other resources that are not allowed to overcommit,
  6514  			// the default value and defaultRequest value must match if both are specified
  6515  			if !helper.IsOvercommitAllowed(core.ResourceName(k)) && defaultQuantityFound && defaultRequestQuantityFound && defaultQuantity.Cmp(defaultRequestQuantity) != 0 {
  6516  				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)))
  6517  			}
  6518  		}
  6519  	}
  6520  
  6521  	return allErrs
  6522  }
  6523  
  6524  // ValidateServiceAccount tests if required fields in the ServiceAccount are set.
  6525  func ValidateServiceAccount(serviceAccount *core.ServiceAccount) field.ErrorList {
  6526  	allErrs := ValidateObjectMeta(&serviceAccount.ObjectMeta, true, ValidateServiceAccountName, field.NewPath("metadata"))
  6527  	return allErrs
  6528  }
  6529  
  6530  // ValidateServiceAccountUpdate tests if required fields in the ServiceAccount are set.
  6531  func ValidateServiceAccountUpdate(newServiceAccount, oldServiceAccount *core.ServiceAccount) field.ErrorList {
  6532  	allErrs := ValidateObjectMetaUpdate(&newServiceAccount.ObjectMeta, &oldServiceAccount.ObjectMeta, field.NewPath("metadata"))
  6533  	allErrs = append(allErrs, ValidateServiceAccount(newServiceAccount)...)
  6534  	return allErrs
  6535  }
  6536  
  6537  // ValidateSecret tests if required fields in the Secret are set.
  6538  func ValidateSecret(secret *core.Secret) field.ErrorList {
  6539  	allErrs := ValidateObjectMeta(&secret.ObjectMeta, true, ValidateSecretName, field.NewPath("metadata"))
  6540  
  6541  	dataPath := field.NewPath("data")
  6542  	totalSize := 0
  6543  	for key, value := range secret.Data {
  6544  		for _, msg := range validation.IsConfigMapKey(key) {
  6545  			allErrs = append(allErrs, field.Invalid(dataPath.Key(key), key, msg))
  6546  		}
  6547  		totalSize += len(value)
  6548  	}
  6549  	if totalSize > core.MaxSecretSize {
  6550  		allErrs = append(allErrs, field.TooLong(dataPath, "", core.MaxSecretSize))
  6551  	}
  6552  
  6553  	switch secret.Type {
  6554  	case core.SecretTypeServiceAccountToken:
  6555  		// Only require Annotations[kubernetes.io/service-account.name]
  6556  		// Additional fields (like Annotations[kubernetes.io/service-account.uid] and Data[token]) might be contributed later by a controller loop
  6557  		if value := secret.Annotations[core.ServiceAccountNameKey]; len(value) == 0 {
  6558  			allErrs = append(allErrs, field.Required(field.NewPath("metadata", "annotations").Key(core.ServiceAccountNameKey), ""))
  6559  		}
  6560  	case core.SecretTypeOpaque, "":
  6561  	// no-op
  6562  	case core.SecretTypeDockercfg:
  6563  		dockercfgBytes, exists := secret.Data[core.DockerConfigKey]
  6564  		if !exists {
  6565  			allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigKey), ""))
  6566  			break
  6567  		}
  6568  
  6569  		// make sure that the content is well-formed json.
  6570  		if err := json.Unmarshal(dockercfgBytes, &map[string]interface{}{}); err != nil {
  6571  			allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigKey), "<secret contents redacted>", err.Error()))
  6572  		}
  6573  	case core.SecretTypeDockerConfigJSON:
  6574  		dockerConfigJSONBytes, exists := secret.Data[core.DockerConfigJSONKey]
  6575  		if !exists {
  6576  			allErrs = append(allErrs, field.Required(dataPath.Key(core.DockerConfigJSONKey), ""))
  6577  			break
  6578  		}
  6579  
  6580  		// make sure that the content is well-formed json.
  6581  		if err := json.Unmarshal(dockerConfigJSONBytes, &map[string]interface{}{}); err != nil {
  6582  			allErrs = append(allErrs, field.Invalid(dataPath.Key(core.DockerConfigJSONKey), "<secret contents redacted>", err.Error()))
  6583  		}
  6584  	case core.SecretTypeBasicAuth:
  6585  		_, usernameFieldExists := secret.Data[core.BasicAuthUsernameKey]
  6586  		_, passwordFieldExists := secret.Data[core.BasicAuthPasswordKey]
  6587  
  6588  		// username or password might be empty, but the field must be present
  6589  		if !usernameFieldExists && !passwordFieldExists {
  6590  			allErrs = append(allErrs, field.Required(dataPath.Key(core.BasicAuthUsernameKey), ""))
  6591  			allErrs = append(allErrs, field.Required(dataPath.Key(core.BasicAuthPasswordKey), ""))
  6592  			break
  6593  		}
  6594  	case core.SecretTypeSSHAuth:
  6595  		if len(secret.Data[core.SSHAuthPrivateKey]) == 0 {
  6596  			allErrs = append(allErrs, field.Required(dataPath.Key(core.SSHAuthPrivateKey), ""))
  6597  			break
  6598  		}
  6599  
  6600  	case core.SecretTypeTLS:
  6601  		if _, exists := secret.Data[core.TLSCertKey]; !exists {
  6602  			allErrs = append(allErrs, field.Required(dataPath.Key(core.TLSCertKey), ""))
  6603  		}
  6604  		if _, exists := secret.Data[core.TLSPrivateKeyKey]; !exists {
  6605  			allErrs = append(allErrs, field.Required(dataPath.Key(core.TLSPrivateKeyKey), ""))
  6606  		}
  6607  	default:
  6608  		// no-op
  6609  	}
  6610  
  6611  	return allErrs
  6612  }
  6613  
  6614  // ValidateSecretUpdate tests if required fields in the Secret are set.
  6615  func ValidateSecretUpdate(newSecret, oldSecret *core.Secret) field.ErrorList {
  6616  	allErrs := ValidateObjectMetaUpdate(&newSecret.ObjectMeta, &oldSecret.ObjectMeta, field.NewPath("metadata"))
  6617  
  6618  	allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, field.NewPath("type"))...)
  6619  	if oldSecret.Immutable != nil && *oldSecret.Immutable {
  6620  		if newSecret.Immutable == nil || !*newSecret.Immutable {
  6621  			allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set"))
  6622  		}
  6623  		if !reflect.DeepEqual(newSecret.Data, oldSecret.Data) {
  6624  			allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set"))
  6625  		}
  6626  		// We don't validate StringData, as it was already converted back to Data
  6627  		// before validation is happening.
  6628  	}
  6629  
  6630  	allErrs = append(allErrs, ValidateSecret(newSecret)...)
  6631  	return allErrs
  6632  }
  6633  
  6634  // ValidateConfigMapName can be used to check whether the given ConfigMap name is valid.
  6635  // Prefix indicates this name will be used as part of generation, in which case
  6636  // trailing dashes are allowed.
  6637  var ValidateConfigMapName = apimachineryvalidation.NameIsDNSSubdomain
  6638  
  6639  // ValidateConfigMap tests whether required fields in the ConfigMap are set.
  6640  func ValidateConfigMap(cfg *core.ConfigMap) field.ErrorList {
  6641  	allErrs := field.ErrorList{}
  6642  	allErrs = append(allErrs, ValidateObjectMeta(&cfg.ObjectMeta, true, ValidateConfigMapName, field.NewPath("metadata"))...)
  6643  
  6644  	totalSize := 0
  6645  
  6646  	for key, value := range cfg.Data {
  6647  		for _, msg := range validation.IsConfigMapKey(key) {
  6648  			allErrs = append(allErrs, field.Invalid(field.NewPath("data").Key(key), key, msg))
  6649  		}
  6650  		// check if we have a duplicate key in the other bag
  6651  		if _, isValue := cfg.BinaryData[key]; isValue {
  6652  			msg := "duplicate of key present in binaryData"
  6653  			allErrs = append(allErrs, field.Invalid(field.NewPath("data").Key(key), key, msg))
  6654  		}
  6655  		totalSize += len(value)
  6656  	}
  6657  	for key, value := range cfg.BinaryData {
  6658  		for _, msg := range validation.IsConfigMapKey(key) {
  6659  			allErrs = append(allErrs, field.Invalid(field.NewPath("binaryData").Key(key), key, msg))
  6660  		}
  6661  		totalSize += len(value)
  6662  	}
  6663  	if totalSize > core.MaxSecretSize {
  6664  		// pass back "" to indicate that the error refers to the whole object.
  6665  		allErrs = append(allErrs, field.TooLong(field.NewPath(""), cfg, core.MaxSecretSize))
  6666  	}
  6667  
  6668  	return allErrs
  6669  }
  6670  
  6671  // ValidateConfigMapUpdate tests if required fields in the ConfigMap are set.
  6672  func ValidateConfigMapUpdate(newCfg, oldCfg *core.ConfigMap) field.ErrorList {
  6673  	allErrs := field.ErrorList{}
  6674  	allErrs = append(allErrs, ValidateObjectMetaUpdate(&newCfg.ObjectMeta, &oldCfg.ObjectMeta, field.NewPath("metadata"))...)
  6675  
  6676  	if oldCfg.Immutable != nil && *oldCfg.Immutable {
  6677  		if newCfg.Immutable == nil || !*newCfg.Immutable {
  6678  			allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set"))
  6679  		}
  6680  		if !reflect.DeepEqual(newCfg.Data, oldCfg.Data) {
  6681  			allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set"))
  6682  		}
  6683  		if !reflect.DeepEqual(newCfg.BinaryData, oldCfg.BinaryData) {
  6684  			allErrs = append(allErrs, field.Forbidden(field.NewPath("binaryData"), "field is immutable when `immutable` is set"))
  6685  		}
  6686  	}
  6687  
  6688  	allErrs = append(allErrs, ValidateConfigMap(newCfg)...)
  6689  	return allErrs
  6690  }
  6691  
  6692  func validateBasicResource(quantity resource.Quantity, fldPath *field.Path) field.ErrorList {
  6693  	if quantity.Value() < 0 {
  6694  		return field.ErrorList{field.Invalid(fldPath, quantity.Value(), "must be a valid resource quantity")}
  6695  	}
  6696  	return field.ErrorList{}
  6697  }
  6698  
  6699  // Validates resource requirement spec.
  6700  func ValidateResourceRequirements(requirements *core.ResourceRequirements, podClaimNames sets.Set[string], fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  6701  	allErrs := field.ErrorList{}
  6702  	limPath := fldPath.Child("limits")
  6703  	reqPath := fldPath.Child("requests")
  6704  	limContainsCPUOrMemory := false
  6705  	reqContainsCPUOrMemory := false
  6706  	limContainsHugePages := false
  6707  	reqContainsHugePages := false
  6708  	supportedQoSComputeResources := sets.New(core.ResourceCPU, core.ResourceMemory)
  6709  	for resourceName, quantity := range requirements.Limits {
  6710  
  6711  		fldPath := limPath.Key(string(resourceName))
  6712  		// Validate resource name.
  6713  		allErrs = append(allErrs, validateContainerResourceName(resourceName, fldPath)...)
  6714  
  6715  		// Validate resource quantity.
  6716  		allErrs = append(allErrs, ValidateResourceQuantityValue(resourceName, quantity, fldPath)...)
  6717  
  6718  		if helper.IsHugePageResourceName(resourceName) {
  6719  			limContainsHugePages = true
  6720  			if err := validateResourceQuantityHugePageValue(resourceName, quantity, opts); err != nil {
  6721  				allErrs = append(allErrs, field.Invalid(fldPath, quantity.String(), err.Error()))
  6722  			}
  6723  		}
  6724  
  6725  		if supportedQoSComputeResources.Has(resourceName) {
  6726  			limContainsCPUOrMemory = true
  6727  		}
  6728  	}
  6729  	for resourceName, quantity := range requirements.Requests {
  6730  		fldPath := reqPath.Key(string(resourceName))
  6731  		// Validate resource name.
  6732  		allErrs = append(allErrs, validateContainerResourceName(resourceName, fldPath)...)
  6733  		// Validate resource quantity.
  6734  		allErrs = append(allErrs, ValidateResourceQuantityValue(resourceName, quantity, fldPath)...)
  6735  
  6736  		// Check that request <= limit.
  6737  		limitQuantity, exists := requirements.Limits[resourceName]
  6738  		if exists {
  6739  			// For non overcommitable resources, not only requests can't exceed limits, they also can't be lower, i.e. must be equal.
  6740  			if quantity.Cmp(limitQuantity) != 0 && !helper.IsOvercommitAllowed(resourceName) {
  6741  				allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be equal to %s limit of %s", resourceName, limitQuantity.String())))
  6742  			} else if quantity.Cmp(limitQuantity) > 0 {
  6743  				allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be less than or equal to %s limit of %s", resourceName, limitQuantity.String())))
  6744  			}
  6745  		} else if !helper.IsOvercommitAllowed(resourceName) {
  6746  			allErrs = append(allErrs, field.Required(limPath, "Limit must be set for non overcommitable resources"))
  6747  		}
  6748  		if helper.IsHugePageResourceName(resourceName) {
  6749  			reqContainsHugePages = true
  6750  			if err := validateResourceQuantityHugePageValue(resourceName, quantity, opts); err != nil {
  6751  				allErrs = append(allErrs, field.Invalid(fldPath, quantity.String(), err.Error()))
  6752  			}
  6753  		}
  6754  		if supportedQoSComputeResources.Has(resourceName) {
  6755  			reqContainsCPUOrMemory = true
  6756  		}
  6757  
  6758  	}
  6759  	if !limContainsCPUOrMemory && !reqContainsCPUOrMemory && (reqContainsHugePages || limContainsHugePages) {
  6760  		allErrs = append(allErrs, field.Forbidden(fldPath, "HugePages require cpu or memory"))
  6761  	}
  6762  
  6763  	allErrs = append(allErrs, validateResourceClaimNames(requirements.Claims, podClaimNames, fldPath.Child("claims"))...)
  6764  
  6765  	return allErrs
  6766  }
  6767  
  6768  // validateResourceClaimNames checks that the names in
  6769  // ResourceRequirements.Claims have a corresponding entry in
  6770  // PodSpec.ResourceClaims.
  6771  func validateResourceClaimNames(claims []core.ResourceClaim, podClaimNames sets.Set[string], fldPath *field.Path) field.ErrorList {
  6772  	var allErrs field.ErrorList
  6773  	names := sets.Set[string]{}
  6774  	for i, claim := range claims {
  6775  		name := claim.Name
  6776  		if name == "" {
  6777  			allErrs = append(allErrs, field.Required(fldPath.Index(i), ""))
  6778  		} else {
  6779  			if names.Has(name) {
  6780  				allErrs = append(allErrs, field.Duplicate(fldPath.Index(i), name))
  6781  			} else {
  6782  				names.Insert(name)
  6783  			}
  6784  			if !podClaimNames.Has(name) {
  6785  				// field.NotFound doesn't accept an
  6786  				// explanation. Adding one here is more
  6787  				// user-friendly.
  6788  				error := field.NotFound(fldPath.Index(i), name)
  6789  				error.Detail = "must be one of the names in pod.spec.resourceClaims"
  6790  				if len(podClaimNames) == 0 {
  6791  					error.Detail += " which is empty"
  6792  				} else {
  6793  					error.Detail += ": " + strings.Join(sets.List(podClaimNames), ", ")
  6794  				}
  6795  				allErrs = append(allErrs, error)
  6796  			}
  6797  		}
  6798  	}
  6799  	return allErrs
  6800  }
  6801  
  6802  func validateResourceQuantityHugePageValue(name core.ResourceName, quantity resource.Quantity, opts PodValidationOptions) error {
  6803  	if !helper.IsHugePageResourceName(name) {
  6804  		return nil
  6805  	}
  6806  
  6807  	if !opts.AllowIndivisibleHugePagesValues && !helper.IsHugePageResourceValueDivisible(name, quantity) {
  6808  		return fmt.Errorf("%s is not positive integer multiple of %s", quantity.String(), name)
  6809  	}
  6810  
  6811  	return nil
  6812  }
  6813  
  6814  // validateResourceQuotaScopes ensures that each enumerated hard resource constraint is valid for set of scopes
  6815  func validateResourceQuotaScopes(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6816  	allErrs := field.ErrorList{}
  6817  	if len(resourceQuotaSpec.Scopes) == 0 {
  6818  		return allErrs
  6819  	}
  6820  	hardLimits := sets.New[core.ResourceName]()
  6821  	for k := range resourceQuotaSpec.Hard {
  6822  		hardLimits.Insert(k)
  6823  	}
  6824  	fldPath := fld.Child("scopes")
  6825  	scopeSet := sets.New[core.ResourceQuotaScope]()
  6826  	for _, scope := range resourceQuotaSpec.Scopes {
  6827  		if !helper.IsStandardResourceQuotaScope(scope) {
  6828  			allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "unsupported scope"))
  6829  		}
  6830  		for _, k := range sets.List(hardLimits) {
  6831  			if helper.IsStandardQuotaResourceName(k) && !helper.IsResourceQuotaScopeValidForResource(scope, k) {
  6832  				allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "unsupported scope applied to resource"))
  6833  			}
  6834  		}
  6835  		scopeSet.Insert(scope)
  6836  	}
  6837  	invalidScopePairs := []sets.Set[core.ResourceQuotaScope]{
  6838  		sets.New(core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort),
  6839  		sets.New(core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating),
  6840  	}
  6841  	for _, invalidScopePair := range invalidScopePairs {
  6842  		if scopeSet.HasAll(sets.List(invalidScopePair)...) {
  6843  			allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "conflicting scopes"))
  6844  		}
  6845  	}
  6846  	return allErrs
  6847  }
  6848  
  6849  // validateScopedResourceSelectorRequirement tests that the match expressions has valid data
  6850  func validateScopedResourceSelectorRequirement(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6851  	allErrs := field.ErrorList{}
  6852  	hardLimits := sets.New[core.ResourceName]()
  6853  	for k := range resourceQuotaSpec.Hard {
  6854  		hardLimits.Insert(k)
  6855  	}
  6856  	fldPath := fld.Child("matchExpressions")
  6857  	scopeSet := sets.New[core.ResourceQuotaScope]()
  6858  	for _, req := range resourceQuotaSpec.ScopeSelector.MatchExpressions {
  6859  		if !helper.IsStandardResourceQuotaScope(req.ScopeName) {
  6860  			allErrs = append(allErrs, field.Invalid(fldPath.Child("scopeName"), req.ScopeName, "unsupported scope"))
  6861  		}
  6862  		for _, k := range sets.List(hardLimits) {
  6863  			if helper.IsStandardQuotaResourceName(k) && !helper.IsResourceQuotaScopeValidForResource(req.ScopeName, k) {
  6864  				allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.ScopeSelector, "unsupported scope applied to resource"))
  6865  			}
  6866  		}
  6867  		switch req.ScopeName {
  6868  		case core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort, core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating, core.ResourceQuotaScopeCrossNamespacePodAffinity:
  6869  			if req.Operator != core.ScopeSelectorOpExists {
  6870  				allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator,
  6871  					"must be 'Exist' when scope is any of ResourceQuotaScopeTerminating, ResourceQuotaScopeNotTerminating, ResourceQuotaScopeBestEffort, ResourceQuotaScopeNotBestEffort or ResourceQuotaScopeCrossNamespacePodAffinity"))
  6872  			}
  6873  		}
  6874  
  6875  		switch req.Operator {
  6876  		case core.ScopeSelectorOpIn, core.ScopeSelectorOpNotIn:
  6877  			if len(req.Values) == 0 {
  6878  				allErrs = append(allErrs, field.Required(fldPath.Child("values"),
  6879  					"must be at least one value when `operator` is 'In' or 'NotIn' for scope selector"))
  6880  			}
  6881  		case core.ScopeSelectorOpExists, core.ScopeSelectorOpDoesNotExist:
  6882  			if len(req.Values) != 0 {
  6883  				allErrs = append(allErrs, field.Invalid(fldPath.Child("values"), req.Values,
  6884  					"must be no value when `operator` is 'Exist' or 'DoesNotExist' for scope selector"))
  6885  			}
  6886  		default:
  6887  			allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), req.Operator, "not a valid selector operator"))
  6888  		}
  6889  		scopeSet.Insert(req.ScopeName)
  6890  	}
  6891  	invalidScopePairs := []sets.Set[core.ResourceQuotaScope]{
  6892  		sets.New(core.ResourceQuotaScopeBestEffort, core.ResourceQuotaScopeNotBestEffort),
  6893  		sets.New(core.ResourceQuotaScopeTerminating, core.ResourceQuotaScopeNotTerminating),
  6894  	}
  6895  	for _, invalidScopePair := range invalidScopePairs {
  6896  		if scopeSet.HasAll(sets.List(invalidScopePair)...) {
  6897  			allErrs = append(allErrs, field.Invalid(fldPath, resourceQuotaSpec.Scopes, "conflicting scopes"))
  6898  		}
  6899  	}
  6900  
  6901  	return allErrs
  6902  }
  6903  
  6904  // validateScopeSelector tests that the specified scope selector has valid data
  6905  func validateScopeSelector(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6906  	allErrs := field.ErrorList{}
  6907  	if resourceQuotaSpec.ScopeSelector == nil {
  6908  		return allErrs
  6909  	}
  6910  	allErrs = append(allErrs, validateScopedResourceSelectorRequirement(resourceQuotaSpec, fld.Child("scopeSelector"))...)
  6911  	return allErrs
  6912  }
  6913  
  6914  // ValidateResourceQuota tests if required fields in the ResourceQuota are set.
  6915  func ValidateResourceQuota(resourceQuota *core.ResourceQuota) field.ErrorList {
  6916  	allErrs := ValidateObjectMeta(&resourceQuota.ObjectMeta, true, ValidateResourceQuotaName, field.NewPath("metadata"))
  6917  
  6918  	allErrs = append(allErrs, ValidateResourceQuotaSpec(&resourceQuota.Spec, field.NewPath("spec"))...)
  6919  	allErrs = append(allErrs, ValidateResourceQuotaStatus(&resourceQuota.Status, field.NewPath("status"))...)
  6920  
  6921  	return allErrs
  6922  }
  6923  
  6924  func ValidateResourceQuotaStatus(status *core.ResourceQuotaStatus, fld *field.Path) field.ErrorList {
  6925  	allErrs := field.ErrorList{}
  6926  
  6927  	fldPath := fld.Child("hard")
  6928  	for k, v := range status.Hard {
  6929  		resPath := fldPath.Key(string(k))
  6930  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6931  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6932  	}
  6933  	fldPath = fld.Child("used")
  6934  	for k, v := range status.Used {
  6935  		resPath := fldPath.Key(string(k))
  6936  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6937  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6938  	}
  6939  
  6940  	return allErrs
  6941  }
  6942  
  6943  func ValidateResourceQuotaSpec(resourceQuotaSpec *core.ResourceQuotaSpec, fld *field.Path) field.ErrorList {
  6944  	allErrs := field.ErrorList{}
  6945  
  6946  	fldPath := fld.Child("hard")
  6947  	for k, v := range resourceQuotaSpec.Hard {
  6948  		resPath := fldPath.Key(string(k))
  6949  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  6950  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  6951  	}
  6952  
  6953  	allErrs = append(allErrs, validateResourceQuotaScopes(resourceQuotaSpec, fld)...)
  6954  	allErrs = append(allErrs, validateScopeSelector(resourceQuotaSpec, fld)...)
  6955  
  6956  	return allErrs
  6957  }
  6958  
  6959  // ValidateResourceQuantityValue enforces that specified quantity is valid for specified resource
  6960  func ValidateResourceQuantityValue(resource core.ResourceName, value resource.Quantity, fldPath *field.Path) field.ErrorList {
  6961  	allErrs := field.ErrorList{}
  6962  	allErrs = append(allErrs, ValidateNonnegativeQuantity(value, fldPath)...)
  6963  	if helper.IsIntegerResourceName(resource) {
  6964  		if value.MilliValue()%int64(1000) != int64(0) {
  6965  			allErrs = append(allErrs, field.Invalid(fldPath, value, isNotIntegerErrorMsg))
  6966  		}
  6967  	}
  6968  	return allErrs
  6969  }
  6970  
  6971  // ValidateResourceQuotaUpdate tests to see if the update is legal for an end user to make.
  6972  func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
  6973  	allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
  6974  	allErrs = append(allErrs, ValidateResourceQuotaSpec(&newResourceQuota.Spec, field.NewPath("spec"))...)
  6975  
  6976  	// ensure scopes cannot change, and that resources are still valid for scope
  6977  	fldPath := field.NewPath("spec", "scopes")
  6978  	oldScopes := sets.New[string]()
  6979  	newScopes := sets.New[string]()
  6980  	for _, scope := range newResourceQuota.Spec.Scopes {
  6981  		newScopes.Insert(string(scope))
  6982  	}
  6983  	for _, scope := range oldResourceQuota.Spec.Scopes {
  6984  		oldScopes.Insert(string(scope))
  6985  	}
  6986  	if !oldScopes.Equal(newScopes) {
  6987  		allErrs = append(allErrs, field.Invalid(fldPath, newResourceQuota.Spec.Scopes, fieldImmutableErrorMsg))
  6988  	}
  6989  
  6990  	return allErrs
  6991  }
  6992  
  6993  // ValidateResourceQuotaStatusUpdate tests to see if the status update is legal for an end user to make.
  6994  func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
  6995  	allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
  6996  	if len(newResourceQuota.ResourceVersion) == 0 {
  6997  		allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
  6998  	}
  6999  	fldPath := field.NewPath("status", "hard")
  7000  	for k, v := range newResourceQuota.Status.Hard {
  7001  		resPath := fldPath.Key(string(k))
  7002  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  7003  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  7004  	}
  7005  	fldPath = field.NewPath("status", "used")
  7006  	for k, v := range newResourceQuota.Status.Used {
  7007  		resPath := fldPath.Key(string(k))
  7008  		allErrs = append(allErrs, ValidateResourceQuotaResourceName(k, resPath)...)
  7009  		allErrs = append(allErrs, ValidateResourceQuantityValue(k, v, resPath)...)
  7010  	}
  7011  	return allErrs
  7012  }
  7013  
  7014  // ValidateNamespace tests if required fields are set.
  7015  func ValidateNamespace(namespace *core.Namespace) field.ErrorList {
  7016  	allErrs := ValidateObjectMeta(&namespace.ObjectMeta, false, ValidateNamespaceName, field.NewPath("metadata"))
  7017  	for i := range namespace.Spec.Finalizers {
  7018  		allErrs = append(allErrs, validateFinalizerName(string(namespace.Spec.Finalizers[i]), field.NewPath("spec", "finalizers"))...)
  7019  	}
  7020  	return allErrs
  7021  }
  7022  
  7023  // Validate finalizer names
  7024  func validateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
  7025  	allErrs := apimachineryvalidation.ValidateFinalizerName(stringValue, fldPath)
  7026  	allErrs = append(allErrs, validateKubeFinalizerName(stringValue, fldPath)...)
  7027  	return allErrs
  7028  }
  7029  
  7030  // validateKubeFinalizerName checks for "standard" names of legacy finalizer
  7031  func validateKubeFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
  7032  	allErrs := field.ErrorList{}
  7033  	if len(strings.Split(stringValue, "/")) == 1 {
  7034  		if !helper.IsStandardFinalizerName(stringValue) {
  7035  			return append(allErrs, field.Invalid(fldPath, stringValue, "name is neither a standard finalizer name nor is it fully qualified"))
  7036  		}
  7037  	}
  7038  
  7039  	return allErrs
  7040  }
  7041  
  7042  // ValidateNamespaceUpdate tests to make sure a namespace update can be applied.
  7043  func ValidateNamespaceUpdate(newNamespace *core.Namespace, oldNamespace *core.Namespace) field.ErrorList {
  7044  	allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  7045  	return allErrs
  7046  }
  7047  
  7048  // ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make.
  7049  func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
  7050  	allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  7051  	if newNamespace.DeletionTimestamp.IsZero() {
  7052  		if newNamespace.Status.Phase != core.NamespaceActive {
  7053  			allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Active' if `deletionTimestamp` is empty"))
  7054  		}
  7055  	} else {
  7056  		if newNamespace.Status.Phase != core.NamespaceTerminating {
  7057  			allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Terminating' if `deletionTimestamp` is not empty"))
  7058  		}
  7059  	}
  7060  	return allErrs
  7061  }
  7062  
  7063  // ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make.
  7064  func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
  7065  	allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
  7066  
  7067  	fldPath := field.NewPath("spec", "finalizers")
  7068  	for i := range newNamespace.Spec.Finalizers {
  7069  		idxPath := fldPath.Index(i)
  7070  		allErrs = append(allErrs, validateFinalizerName(string(newNamespace.Spec.Finalizers[i]), idxPath)...)
  7071  	}
  7072  	return allErrs
  7073  }
  7074  
  7075  // ValidateEndpoints validates Endpoints on create and update.
  7076  func ValidateEndpoints(endpoints *core.Endpoints) field.ErrorList {
  7077  	allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
  7078  	allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
  7079  	allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...)
  7080  	return allErrs
  7081  }
  7082  
  7083  // ValidateEndpointsCreate validates Endpoints on create.
  7084  func ValidateEndpointsCreate(endpoints *core.Endpoints) field.ErrorList {
  7085  	return ValidateEndpoints(endpoints)
  7086  }
  7087  
  7088  // ValidateEndpointsUpdate validates Endpoints on update. NodeName changes are
  7089  // allowed during update to accommodate the case where nodeIP or PodCIDR is
  7090  // reused. An existing endpoint ip will have a different nodeName if this
  7091  // happens.
  7092  func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *core.Endpoints) field.ErrorList {
  7093  	allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
  7094  	allErrs = append(allErrs, ValidateEndpoints(newEndpoints)...)
  7095  	return allErrs
  7096  }
  7097  
  7098  func validateEndpointSubsets(subsets []core.EndpointSubset, fldPath *field.Path) field.ErrorList {
  7099  	allErrs := field.ErrorList{}
  7100  	for i := range subsets {
  7101  		ss := &subsets[i]
  7102  		idxPath := fldPath.Index(i)
  7103  
  7104  		// EndpointSubsets must include endpoint address. For headless service, we allow its endpoints not to have ports.
  7105  		if len(ss.Addresses) == 0 && len(ss.NotReadyAddresses) == 0 {
  7106  			// TODO: consider adding a RequiredOneOf() error for this and similar cases
  7107  			allErrs = append(allErrs, field.Required(idxPath, "must specify `addresses` or `notReadyAddresses`"))
  7108  		}
  7109  		for addr := range ss.Addresses {
  7110  			allErrs = append(allErrs, validateEndpointAddress(&ss.Addresses[addr], idxPath.Child("addresses").Index(addr))...)
  7111  		}
  7112  		for addr := range ss.NotReadyAddresses {
  7113  			allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...)
  7114  		}
  7115  		for port := range ss.Ports {
  7116  			allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
  7117  		}
  7118  	}
  7119  
  7120  	return allErrs
  7121  }
  7122  
  7123  func validateEndpointAddress(address *core.EndpointAddress, fldPath *field.Path) field.ErrorList {
  7124  	allErrs := field.ErrorList{}
  7125  	allErrs = append(allErrs, validation.IsValidIP(fldPath.Child("ip"), address.IP)...)
  7126  	if len(address.Hostname) > 0 {
  7127  		allErrs = append(allErrs, ValidateDNS1123Label(address.Hostname, fldPath.Child("hostname"))...)
  7128  	}
  7129  	// During endpoint update, verify that NodeName is a DNS subdomain and transition rules allow the update
  7130  	if address.NodeName != nil {
  7131  		for _, msg := range ValidateNodeName(*address.NodeName, false) {
  7132  			allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), *address.NodeName, msg))
  7133  		}
  7134  	}
  7135  	allErrs = append(allErrs, ValidateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
  7136  	return allErrs
  7137  }
  7138  
  7139  // ValidateNonSpecialIP is used to validate Endpoints, EndpointSlices, and
  7140  // external IPs. Specifically, this disallows unspecified and loopback addresses
  7141  // are nonsensical and link-local addresses tend to be used for node-centric
  7142  // purposes (e.g. metadata service).
  7143  //
  7144  // IPv6 references
  7145  // - https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
  7146  // - https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
  7147  func ValidateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
  7148  	allErrs := field.ErrorList{}
  7149  	ip := netutils.ParseIPSloppy(ipAddress)
  7150  	if ip == nil {
  7151  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "must be a valid IP address"))
  7152  		return allErrs
  7153  	}
  7154  	if ip.IsUnspecified() {
  7155  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, fmt.Sprintf("may not be unspecified (%v)", ipAddress)))
  7156  	}
  7157  	if ip.IsLoopback() {
  7158  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the loopback range (127.0.0.0/8, ::1/128)"))
  7159  	}
  7160  	if ip.IsLinkLocalUnicast() {
  7161  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local range (169.254.0.0/16, fe80::/10)"))
  7162  	}
  7163  	if ip.IsLinkLocalMulticast() {
  7164  		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the link-local multicast range (224.0.0.0/24, ff02::/10)"))
  7165  	}
  7166  	return allErrs
  7167  }
  7168  
  7169  func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *field.Path) field.ErrorList {
  7170  	allErrs := field.ErrorList{}
  7171  	if requireName && len(port.Name) == 0 {
  7172  		allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
  7173  	} else if len(port.Name) != 0 {
  7174  		allErrs = append(allErrs, ValidateDNS1123Label(port.Name, fldPath.Child("name"))...)
  7175  	}
  7176  	for _, msg := range validation.IsValidPortNum(int(port.Port)) {
  7177  		allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), port.Port, msg))
  7178  	}
  7179  	if len(port.Protocol) == 0 {
  7180  		allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
  7181  	} else if !supportedPortProtocols.Has(port.Protocol) {
  7182  		allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, sets.List(supportedPortProtocols)))
  7183  	}
  7184  	if port.AppProtocol != nil {
  7185  		allErrs = append(allErrs, ValidateQualifiedName(*port.AppProtocol, fldPath.Child("appProtocol"))...)
  7186  	}
  7187  	return allErrs
  7188  }
  7189  
  7190  // ValidateSecurityContext ensures the security context contains valid settings
  7191  func ValidateSecurityContext(sc *core.SecurityContext, fldPath *field.Path, hostUsers bool) field.ErrorList {
  7192  	allErrs := field.ErrorList{}
  7193  	// this should only be true for testing since SecurityContext is defaulted by the core
  7194  	if sc == nil {
  7195  		return allErrs
  7196  	}
  7197  
  7198  	if sc.Privileged != nil {
  7199  		if *sc.Privileged && !capabilities.Get().AllowPrivileged {
  7200  			allErrs = append(allErrs, field.Forbidden(fldPath.Child("privileged"), "disallowed by cluster policy"))
  7201  		}
  7202  	}
  7203  
  7204  	if sc.RunAsUser != nil {
  7205  		for _, msg := range validation.IsValidUserID(*sc.RunAsUser) {
  7206  			allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *sc.RunAsUser, msg))
  7207  		}
  7208  	}
  7209  
  7210  	if sc.RunAsGroup != nil {
  7211  		for _, msg := range validation.IsValidGroupID(*sc.RunAsGroup) {
  7212  			allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsGroup"), *sc.RunAsGroup, msg))
  7213  		}
  7214  	}
  7215  
  7216  	if sc.ProcMount != nil {
  7217  		if err := ValidateProcMountType(fldPath.Child("procMount"), *sc.ProcMount); err != nil {
  7218  			allErrs = append(allErrs, err)
  7219  		}
  7220  		if hostUsers && *sc.ProcMount == core.UnmaskedProcMount {
  7221  			allErrs = append(allErrs, field.Invalid(fldPath.Child("procMount"), sc.ProcMount, "`hostUsers` must be false to use `Unmasked`"))
  7222  		}
  7223  
  7224  	}
  7225  	allErrs = append(allErrs, validateSeccompProfileField(sc.SeccompProfile, fldPath.Child("seccompProfile"))...)
  7226  	if sc.AllowPrivilegeEscalation != nil && !*sc.AllowPrivilegeEscalation {
  7227  		if sc.Privileged != nil && *sc.Privileged {
  7228  			allErrs = append(allErrs, field.Invalid(fldPath, sc, "cannot set `allowPrivilegeEscalation` to false and `privileged` to true"))
  7229  		}
  7230  
  7231  		if sc.Capabilities != nil {
  7232  			for _, cap := range sc.Capabilities.Add {
  7233  				if string(cap) == "CAP_SYS_ADMIN" {
  7234  					allErrs = append(allErrs, field.Invalid(fldPath, sc, "cannot set `allowPrivilegeEscalation` to false and `capabilities.Add` CAP_SYS_ADMIN"))
  7235  				}
  7236  			}
  7237  		}
  7238  	}
  7239  
  7240  	allErrs = append(allErrs, validateWindowsSecurityContextOptions(sc.WindowsOptions, fldPath.Child("windowsOptions"))...)
  7241  	allErrs = append(allErrs, ValidateAppArmorProfileField(sc.AppArmorProfile, fldPath.Child("appArmorProfile"))...)
  7242  
  7243  	return allErrs
  7244  }
  7245  
  7246  // maxGMSACredentialSpecLength is the max length, in bytes, for the actual contents
  7247  // of a GMSA cred spec. In general, those shouldn't be more than a few hundred bytes,
  7248  // so we want to give plenty of room here while still providing an upper bound.
  7249  // The runAsUserName field will be used to execute the given container's entrypoint, and
  7250  // it can be formatted as "DOMAIN/USER", where the DOMAIN is optional, maxRunAsUserNameDomainLength
  7251  // is the max character length for the user's DOMAIN, and maxRunAsUserNameUserLength
  7252  // is the max character length for the USER itself. Both the DOMAIN and USER have their
  7253  // own restrictions, and more information about them can be found here:
  7254  // https://support.microsoft.com/en-us/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
  7255  // https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/bb726984(v=technet.10)
  7256  const (
  7257  	maxGMSACredentialSpecLengthInKiB = 64
  7258  	maxGMSACredentialSpecLength      = maxGMSACredentialSpecLengthInKiB * 1024
  7259  	maxRunAsUserNameDomainLength     = 256
  7260  	maxRunAsUserNameUserLength       = 104
  7261  )
  7262  
  7263  var (
  7264  	// control characters are not permitted in the runAsUserName field.
  7265  	ctrlRegex = regexp.MustCompile(`[[:cntrl:]]+`)
  7266  
  7267  	// a valid NetBios Domain name cannot start with a dot, has at least 1 character,
  7268  	// at most 15 characters, and it cannot the characters: \ / : * ? " < > |
  7269  	validNetBiosRegex = regexp.MustCompile(`^[^\\/:\*\?"<>|\.][^\\/:\*\?"<>|]{0,14}$`)
  7270  
  7271  	// a valid DNS name contains only alphanumeric characters, dots, and dashes.
  7272  	dnsLabelFormat                 = `[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?`
  7273  	dnsSubdomainFormat             = fmt.Sprintf(`^%s(?:\.%s)*$`, dnsLabelFormat, dnsLabelFormat)
  7274  	validWindowsUserDomainDNSRegex = regexp.MustCompile(dnsSubdomainFormat)
  7275  
  7276  	// a username is invalid if it contains the characters: " / \ [ ] : ; | = , + * ? < > @
  7277  	// or it contains only dots or spaces.
  7278  	invalidUserNameCharsRegex      = regexp.MustCompile(`["/\\:;|=,\+\*\?<>@\[\]]`)
  7279  	invalidUserNameDotsSpacesRegex = regexp.MustCompile(`^[\. ]+$`)
  7280  )
  7281  
  7282  func validateWindowsSecurityContextOptions(windowsOptions *core.WindowsSecurityContextOptions, fieldPath *field.Path) field.ErrorList {
  7283  	allErrs := field.ErrorList{}
  7284  
  7285  	if windowsOptions == nil {
  7286  		return allErrs
  7287  	}
  7288  
  7289  	if windowsOptions.GMSACredentialSpecName != nil {
  7290  		// gmsaCredentialSpecName must be the name of a custom resource
  7291  		for _, msg := range validation.IsDNS1123Subdomain(*windowsOptions.GMSACredentialSpecName) {
  7292  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpecName"), windowsOptions.GMSACredentialSpecName, msg))
  7293  		}
  7294  	}
  7295  
  7296  	if windowsOptions.GMSACredentialSpec != nil {
  7297  		if l := len(*windowsOptions.GMSACredentialSpec); l == 0 {
  7298  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpec"), windowsOptions.GMSACredentialSpec, "gmsaCredentialSpec cannot be an empty string"))
  7299  		} else if l > maxGMSACredentialSpecLength {
  7300  			errMsg := fmt.Sprintf("gmsaCredentialSpec size must be under %d KiB", maxGMSACredentialSpecLengthInKiB)
  7301  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("gmsaCredentialSpec"), windowsOptions.GMSACredentialSpec, errMsg))
  7302  		}
  7303  	}
  7304  
  7305  	if windowsOptions.RunAsUserName != nil {
  7306  		if l := len(*windowsOptions.RunAsUserName); l == 0 {
  7307  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, "runAsUserName cannot be an empty string"))
  7308  		} else if ctrlRegex.MatchString(*windowsOptions.RunAsUserName) {
  7309  			errMsg := "runAsUserName cannot contain control characters"
  7310  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7311  		} else if parts := strings.Split(*windowsOptions.RunAsUserName, "\\"); len(parts) > 2 {
  7312  			errMsg := "runAsUserName cannot contain more than one backslash"
  7313  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7314  		} else {
  7315  			var (
  7316  				hasDomain = false
  7317  				domain    = ""
  7318  				user      string
  7319  			)
  7320  			if len(parts) == 1 {
  7321  				user = parts[0]
  7322  			} else {
  7323  				hasDomain = true
  7324  				domain = parts[0]
  7325  				user = parts[1]
  7326  			}
  7327  
  7328  			if len(domain) >= maxRunAsUserNameDomainLength {
  7329  				errMsg := fmt.Sprintf("runAsUserName's Domain length must be under %d characters", maxRunAsUserNameDomainLength)
  7330  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7331  			}
  7332  
  7333  			if hasDomain && !(validNetBiosRegex.MatchString(domain) || validWindowsUserDomainDNSRegex.MatchString(domain)) {
  7334  				errMsg := "runAsUserName's Domain doesn't match the NetBios nor the DNS format"
  7335  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7336  			}
  7337  
  7338  			if l := len(user); l == 0 {
  7339  				errMsg := "runAsUserName's User cannot be empty"
  7340  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7341  			} else if l > maxRunAsUserNameUserLength {
  7342  				errMsg := fmt.Sprintf("runAsUserName's User length must not be longer than %d characters", maxRunAsUserNameUserLength)
  7343  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7344  			}
  7345  
  7346  			if invalidUserNameDotsSpacesRegex.MatchString(user) {
  7347  				errMsg := `runAsUserName's User cannot contain only periods or spaces`
  7348  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7349  			}
  7350  
  7351  			if invalidUserNameCharsRegex.MatchString(user) {
  7352  				errMsg := `runAsUserName's User cannot contain the following characters: "/\:;|=,+*?<>@[]`
  7353  				allErrs = append(allErrs, field.Invalid(fieldPath.Child("runAsUserName"), windowsOptions.RunAsUserName, errMsg))
  7354  			}
  7355  		}
  7356  	}
  7357  
  7358  	return allErrs
  7359  }
  7360  
  7361  func validateWindowsHostProcessPod(podSpec *core.PodSpec, fieldPath *field.Path) field.ErrorList {
  7362  	allErrs := field.ErrorList{}
  7363  
  7364  	// Keep track of container and hostProcess container count for validate
  7365  	containerCount := 0
  7366  	hostProcessContainerCount := 0
  7367  
  7368  	var podHostProcess *bool
  7369  	if podSpec.SecurityContext != nil && podSpec.SecurityContext.WindowsOptions != nil {
  7370  		podHostProcess = podSpec.SecurityContext.WindowsOptions.HostProcess
  7371  	}
  7372  
  7373  	hostNetwork := false
  7374  	if podSpec.SecurityContext != nil {
  7375  		hostNetwork = podSpec.SecurityContext.HostNetwork
  7376  	}
  7377  
  7378  	podshelper.VisitContainersWithPath(podSpec, fieldPath, func(c *core.Container, cFieldPath *field.Path) bool {
  7379  		containerCount++
  7380  
  7381  		var containerHostProcess *bool = nil
  7382  		if c.SecurityContext != nil && c.SecurityContext.WindowsOptions != nil {
  7383  			containerHostProcess = c.SecurityContext.WindowsOptions.HostProcess
  7384  		}
  7385  
  7386  		if podHostProcess != nil && containerHostProcess != nil && *podHostProcess != *containerHostProcess {
  7387  			errMsg := fmt.Sprintf("pod hostProcess value must be identical if both are specified, was %v", *podHostProcess)
  7388  			allErrs = append(allErrs, field.Invalid(cFieldPath.Child("securityContext", "windowsOptions", "hostProcess"), *containerHostProcess, errMsg))
  7389  		}
  7390  
  7391  		switch {
  7392  		case containerHostProcess != nil && *containerHostProcess:
  7393  			// Container explicitly sets hostProcess=true
  7394  			hostProcessContainerCount++
  7395  		case containerHostProcess == nil && podHostProcess != nil && *podHostProcess:
  7396  			// Container inherits hostProcess=true from pod settings
  7397  			hostProcessContainerCount++
  7398  		}
  7399  
  7400  		return true
  7401  	})
  7402  
  7403  	if hostProcessContainerCount > 0 {
  7404  		// At present, if a Windows Pods contains any HostProcess containers than all containers must be
  7405  		// HostProcess containers (explicitly set or inherited).
  7406  		if hostProcessContainerCount != containerCount {
  7407  			errMsg := "If pod contains any hostProcess containers then all containers must be HostProcess containers"
  7408  			allErrs = append(allErrs, field.Invalid(fieldPath, "", errMsg))
  7409  		}
  7410  
  7411  		// At present Windows Pods which contain HostProcess containers must also set HostNetwork.
  7412  		if !hostNetwork {
  7413  			errMsg := "hostNetwork must be true if pod contains any hostProcess containers"
  7414  			allErrs = append(allErrs, field.Invalid(fieldPath.Child("hostNetwork"), hostNetwork, errMsg))
  7415  		}
  7416  
  7417  		if !capabilities.Get().AllowPrivileged {
  7418  			errMsg := "hostProcess containers are disallowed by cluster policy"
  7419  			allErrs = append(allErrs, field.Forbidden(fieldPath, errMsg))
  7420  		}
  7421  	}
  7422  
  7423  	return allErrs
  7424  }
  7425  
  7426  // validateOS validates the OS field within pod spec
  7427  func validateOS(podSpec *core.PodSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  7428  	allErrs := field.ErrorList{}
  7429  	os := podSpec.OS
  7430  	if os == nil {
  7431  		return allErrs
  7432  	}
  7433  	if len(os.Name) == 0 {
  7434  		return append(allErrs, field.Required(fldPath.Child("name"), "cannot be empty"))
  7435  	}
  7436  	if !validOS.Has(os.Name) {
  7437  		allErrs = append(allErrs, field.NotSupported(fldPath, os.Name, sets.List(validOS)))
  7438  	}
  7439  	return allErrs
  7440  }
  7441  
  7442  func ValidatePodLogOptions(opts *core.PodLogOptions) field.ErrorList {
  7443  	allErrs := field.ErrorList{}
  7444  	if opts.TailLines != nil && *opts.TailLines < 0 {
  7445  		allErrs = append(allErrs, field.Invalid(field.NewPath("tailLines"), *opts.TailLines, isNegativeErrorMsg))
  7446  	}
  7447  	if opts.LimitBytes != nil && *opts.LimitBytes < 1 {
  7448  		allErrs = append(allErrs, field.Invalid(field.NewPath("limitBytes"), *opts.LimitBytes, "must be greater than 0"))
  7449  	}
  7450  	switch {
  7451  	case opts.SinceSeconds != nil && opts.SinceTime != nil:
  7452  		allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "at most one of `sinceTime` or `sinceSeconds` may be specified"))
  7453  	case opts.SinceSeconds != nil:
  7454  		if *opts.SinceSeconds < 1 {
  7455  			allErrs = append(allErrs, field.Invalid(field.NewPath("sinceSeconds"), *opts.SinceSeconds, "must be greater than 0"))
  7456  		}
  7457  	}
  7458  	return allErrs
  7459  }
  7460  
  7461  var (
  7462  	supportedLoadBalancerIPMode = sets.New(core.LoadBalancerIPModeVIP, core.LoadBalancerIPModeProxy)
  7463  )
  7464  
  7465  // ValidateLoadBalancerStatus validates required fields on a LoadBalancerStatus
  7466  func ValidateLoadBalancerStatus(status *core.LoadBalancerStatus, fldPath *field.Path, spec *core.ServiceSpec) field.ErrorList {
  7467  	allErrs := field.ErrorList{}
  7468  	ingrPath := fldPath.Child("ingress")
  7469  	if !utilfeature.DefaultFeatureGate.Enabled(features.AllowServiceLBStatusOnNonLB) && spec.Type != core.ServiceTypeLoadBalancer && len(status.Ingress) != 0 {
  7470  		allErrs = append(allErrs, field.Forbidden(ingrPath, "may only be used when `spec.type` is 'LoadBalancer'"))
  7471  	} else {
  7472  		for i, ingress := range status.Ingress {
  7473  			idxPath := ingrPath.Index(i)
  7474  			if len(ingress.IP) > 0 {
  7475  				allErrs = append(allErrs, validation.IsValidIP(idxPath.Child("ip"), ingress.IP)...)
  7476  			}
  7477  
  7478  			if utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) && ingress.IPMode == nil {
  7479  				if len(ingress.IP) > 0 {
  7480  					allErrs = append(allErrs, field.Required(idxPath.Child("ipMode"), "must be specified when `ip` is set"))
  7481  				}
  7482  			} else if ingress.IPMode != nil && len(ingress.IP) == 0 {
  7483  				allErrs = append(allErrs, field.Forbidden(idxPath.Child("ipMode"), "may not be specified when `ip` is not set"))
  7484  			} else if ingress.IPMode != nil && !supportedLoadBalancerIPMode.Has(*ingress.IPMode) {
  7485  				allErrs = append(allErrs, field.NotSupported(idxPath.Child("ipMode"), ingress.IPMode, sets.List(supportedLoadBalancerIPMode)))
  7486  			}
  7487  
  7488  			if len(ingress.Hostname) > 0 {
  7489  				for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) {
  7490  					allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
  7491  				}
  7492  				if isIP := (netutils.ParseIPSloppy(ingress.Hostname) != nil); isIP {
  7493  					allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))
  7494  				}
  7495  			}
  7496  		}
  7497  	}
  7498  	return allErrs
  7499  }
  7500  
  7501  // validateVolumeNodeAffinity tests that the PersistentVolume.NodeAffinity has valid data
  7502  // returns:
  7503  // - true if volumeNodeAffinity is set
  7504  // - errorList if there are validation errors
  7505  func validateVolumeNodeAffinity(nodeAffinity *core.VolumeNodeAffinity, fldPath *field.Path) (bool, field.ErrorList) {
  7506  	allErrs := field.ErrorList{}
  7507  
  7508  	if nodeAffinity == nil {
  7509  		return false, allErrs
  7510  	}
  7511  
  7512  	if nodeAffinity.Required != nil {
  7513  		allErrs = append(allErrs, ValidateNodeSelector(nodeAffinity.Required, fldPath.Child("required"))...)
  7514  	} else {
  7515  		allErrs = append(allErrs, field.Required(fldPath.Child("required"), "must specify required node constraints"))
  7516  	}
  7517  
  7518  	return true, allErrs
  7519  }
  7520  
  7521  func IsDecremented(update, old *int32) bool {
  7522  	if update == nil && old != nil {
  7523  		return true
  7524  	}
  7525  	if update == nil || old == nil {
  7526  		return false
  7527  	}
  7528  	return *update < *old
  7529  }
  7530  
  7531  // ValidateProcMountType tests that the argument is a valid ProcMountType.
  7532  func ValidateProcMountType(fldPath *field.Path, procMountType core.ProcMountType) *field.Error {
  7533  	switch procMountType {
  7534  	case core.DefaultProcMount, core.UnmaskedProcMount:
  7535  		return nil
  7536  	default:
  7537  		return field.NotSupported(fldPath, procMountType, []core.ProcMountType{core.DefaultProcMount, core.UnmaskedProcMount})
  7538  	}
  7539  }
  7540  
  7541  var (
  7542  	supportedScheduleActions = sets.New(core.DoNotSchedule, core.ScheduleAnyway)
  7543  )
  7544  
  7545  // validateTopologySpreadConstraints validates given TopologySpreadConstraints.
  7546  func validateTopologySpreadConstraints(constraints []core.TopologySpreadConstraint, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
  7547  	allErrs := field.ErrorList{}
  7548  
  7549  	for i, constraint := range constraints {
  7550  		subFldPath := fldPath.Index(i)
  7551  		if err := ValidateMaxSkew(subFldPath.Child("maxSkew"), constraint.MaxSkew); err != nil {
  7552  			allErrs = append(allErrs, err)
  7553  		}
  7554  		if err := ValidateTopologyKey(subFldPath.Child("topologyKey"), constraint.TopologyKey); err != nil {
  7555  			allErrs = append(allErrs, err)
  7556  		}
  7557  		if err := ValidateWhenUnsatisfiable(subFldPath.Child("whenUnsatisfiable"), constraint.WhenUnsatisfiable); err != nil {
  7558  			allErrs = append(allErrs, err)
  7559  		}
  7560  		// tuple {topologyKey, whenUnsatisfiable} denotes one kind of spread constraint
  7561  		if err := ValidateSpreadConstraintNotRepeat(subFldPath.Child("{topologyKey, whenUnsatisfiable}"), constraint, constraints[i+1:]); err != nil {
  7562  			allErrs = append(allErrs, err)
  7563  		}
  7564  		allErrs = append(allErrs, validateMinDomains(subFldPath.Child("minDomains"), constraint.MinDomains, constraint.WhenUnsatisfiable)...)
  7565  		if err := validateNodeInclusionPolicy(subFldPath.Child("nodeAffinityPolicy"), constraint.NodeAffinityPolicy); err != nil {
  7566  			allErrs = append(allErrs, err)
  7567  		}
  7568  		if err := validateNodeInclusionPolicy(subFldPath.Child("nodeTaintsPolicy"), constraint.NodeTaintsPolicy); err != nil {
  7569  			allErrs = append(allErrs, err)
  7570  		}
  7571  		allErrs = append(allErrs, validateMatchLabelKeysInTopologySpread(subFldPath.Child("matchLabelKeys"), constraint.MatchLabelKeys, constraint.LabelSelector)...)
  7572  		if !opts.AllowInvalidTopologySpreadConstraintLabelSelector {
  7573  			allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(constraint.LabelSelector, unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: false}, subFldPath.Child("labelSelector"))...)
  7574  		}
  7575  	}
  7576  
  7577  	return allErrs
  7578  }
  7579  
  7580  // ValidateMaxSkew tests that the argument is a valid MaxSkew.
  7581  func ValidateMaxSkew(fldPath *field.Path, maxSkew int32) *field.Error {
  7582  	if maxSkew <= 0 {
  7583  		return field.Invalid(fldPath, maxSkew, isNotPositiveErrorMsg)
  7584  	}
  7585  	return nil
  7586  }
  7587  
  7588  // validateMinDomains tests that the argument is a valid MinDomains.
  7589  func validateMinDomains(fldPath *field.Path, minDomains *int32, action core.UnsatisfiableConstraintAction) field.ErrorList {
  7590  	if minDomains == nil {
  7591  		return nil
  7592  	}
  7593  	var allErrs field.ErrorList
  7594  	if *minDomains <= 0 {
  7595  		allErrs = append(allErrs, field.Invalid(fldPath, minDomains, isNotPositiveErrorMsg))
  7596  	}
  7597  	// When MinDomains is non-nil, whenUnsatisfiable must be DoNotSchedule.
  7598  	if action != core.DoNotSchedule {
  7599  		allErrs = append(allErrs, field.Invalid(fldPath, minDomains, fmt.Sprintf("can only use minDomains if whenUnsatisfiable=%s, not %s", core.DoNotSchedule, action)))
  7600  	}
  7601  	return allErrs
  7602  }
  7603  
  7604  // ValidateTopologyKey tests that the argument is a valid TopologyKey.
  7605  func ValidateTopologyKey(fldPath *field.Path, topologyKey string) *field.Error {
  7606  	if len(topologyKey) == 0 {
  7607  		return field.Required(fldPath, "can not be empty")
  7608  	}
  7609  	return nil
  7610  }
  7611  
  7612  // ValidateWhenUnsatisfiable tests that the argument is a valid UnsatisfiableConstraintAction.
  7613  func ValidateWhenUnsatisfiable(fldPath *field.Path, action core.UnsatisfiableConstraintAction) *field.Error {
  7614  	if !supportedScheduleActions.Has(action) {
  7615  		return field.NotSupported(fldPath, action, sets.List(supportedScheduleActions))
  7616  	}
  7617  	return nil
  7618  }
  7619  
  7620  // ValidateSpreadConstraintNotRepeat tests that if `constraint` duplicates with `existingConstraintPairs`
  7621  // on TopologyKey and WhenUnsatisfiable fields.
  7622  func ValidateSpreadConstraintNotRepeat(fldPath *field.Path, constraint core.TopologySpreadConstraint, restingConstraints []core.TopologySpreadConstraint) *field.Error {
  7623  	for _, restingConstraint := range restingConstraints {
  7624  		if constraint.TopologyKey == restingConstraint.TopologyKey &&
  7625  			constraint.WhenUnsatisfiable == restingConstraint.WhenUnsatisfiable {
  7626  			return field.Duplicate(fldPath, fmt.Sprintf("{%v, %v}", constraint.TopologyKey, constraint.WhenUnsatisfiable))
  7627  		}
  7628  	}
  7629  	return nil
  7630  }
  7631  
  7632  var (
  7633  	supportedPodTopologySpreadNodePolicies = sets.New(core.NodeInclusionPolicyIgnore, core.NodeInclusionPolicyHonor)
  7634  )
  7635  
  7636  // validateNodeAffinityPolicy tests that the argument is a valid NodeInclusionPolicy.
  7637  func validateNodeInclusionPolicy(fldPath *field.Path, policy *core.NodeInclusionPolicy) *field.Error {
  7638  	if policy == nil {
  7639  		return nil
  7640  	}
  7641  
  7642  	if !supportedPodTopologySpreadNodePolicies.Has(*policy) {
  7643  		return field.NotSupported(fldPath, policy, sets.List(supportedPodTopologySpreadNodePolicies))
  7644  	}
  7645  	return nil
  7646  }
  7647  
  7648  // validateMatchLabelKeysAndMismatchLabelKeys checks if both matchLabelKeys and mismatchLabelKeys are valid.
  7649  // - validate that all matchLabelKeys and mismatchLabelKeys are valid label names.
  7650  // - validate that the user doens't specify the same key in both matchLabelKeys and labelSelector.
  7651  // - validate that any matchLabelKeys are not duplicated with mismatchLabelKeys.
  7652  func validateMatchLabelKeysAndMismatchLabelKeys(fldPath *field.Path, matchLabelKeys, mismatchLabelKeys []string, labelSelector *metav1.LabelSelector) field.ErrorList {
  7653  	var allErrs field.ErrorList
  7654  	// 1. validate that all matchLabelKeys and mismatchLabelKeys are valid label names.
  7655  	allErrs = append(allErrs, validateLabelKeys(fldPath.Child("matchLabelKeys"), matchLabelKeys, labelSelector)...)
  7656  	allErrs = append(allErrs, validateLabelKeys(fldPath.Child("mismatchLabelKeys"), mismatchLabelKeys, labelSelector)...)
  7657  
  7658  	// 2. validate that the user doens't specify the same key in both matchLabelKeys and labelSelector.
  7659  	// It doesn't make sense to have the labelselector with the key specified in matchLabelKeys
  7660  	// because the matchLabelKeys will be `In` labelSelector which matches with only one value in the key
  7661  	// and we cannot make any further filtering with that key.
  7662  	// On the other hand, we may want to have labelSelector with the key specified in mismatchLabelKeys.
  7663  	// because the mismatchLabelKeys will be `NotIn` labelSelector
  7664  	// and we may want to filter Pods further with other labelSelector with that key.
  7665  
  7666  	// labelKeysMap is keyed by label key and valued by the index of label key in labelKeys.
  7667  	if labelSelector != nil {
  7668  		labelKeysMap := map[string]int{}
  7669  		for i, key := range matchLabelKeys {
  7670  			labelKeysMap[key] = i
  7671  		}
  7672  		labelSelectorKeys := sets.New[string]()
  7673  		for key := range labelSelector.MatchLabels {
  7674  			labelSelectorKeys.Insert(key)
  7675  		}
  7676  		for _, matchExpression := range labelSelector.MatchExpressions {
  7677  			key := matchExpression.Key
  7678  			if i, ok := labelKeysMap[key]; ok && labelSelectorKeys.Has(key) {
  7679  				// Before validateLabelKeysWithSelector is called, the labelSelector has already got the selector created from matchLabelKeys.
  7680  				// Here, we found the duplicate key in labelSelector and the key is specified in labelKeys.
  7681  				// Meaning that the same key is specified in both labelSelector and matchLabelKeys/mismatchLabelKeys.
  7682  				allErrs = append(allErrs, field.Invalid(fldPath.Index(i), key, "exists in both matchLabelKeys and labelSelector"))
  7683  			}
  7684  
  7685  			labelSelectorKeys.Insert(key)
  7686  		}
  7687  	}
  7688  
  7689  	// 3. validate that any matchLabelKeys are not duplicated with mismatchLabelKeys.
  7690  	mismatchLabelKeysSet := sets.New(mismatchLabelKeys...)
  7691  	for i, k := range matchLabelKeys {
  7692  		if mismatchLabelKeysSet.Has(k) {
  7693  			allErrs = append(allErrs, field.Invalid(fldPath.Child("matchLabelKeys").Index(i), k, "exists in both matchLabelKeys and mismatchLabelKeys"))
  7694  		}
  7695  	}
  7696  
  7697  	return allErrs
  7698  }
  7699  
  7700  // validateMatchLabelKeysInTopologySpread tests that the elements are a valid label name and are not already included in labelSelector.
  7701  func validateMatchLabelKeysInTopologySpread(fldPath *field.Path, matchLabelKeys []string, labelSelector *metav1.LabelSelector) field.ErrorList {
  7702  	if len(matchLabelKeys) == 0 {
  7703  		return nil
  7704  	}
  7705  
  7706  	var allErrs field.ErrorList
  7707  	labelSelectorKeys := sets.Set[string]{}
  7708  
  7709  	if labelSelector != nil {
  7710  		for key := range labelSelector.MatchLabels {
  7711  			labelSelectorKeys.Insert(key)
  7712  		}
  7713  		for _, matchExpression := range labelSelector.MatchExpressions {
  7714  			labelSelectorKeys.Insert(matchExpression.Key)
  7715  		}
  7716  	} else {
  7717  		allErrs = append(allErrs, field.Forbidden(fldPath, "must not be specified when labelSelector is not set"))
  7718  	}
  7719  
  7720  	for i, key := range matchLabelKeys {
  7721  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(key, fldPath.Index(i))...)
  7722  		if labelSelectorKeys.Has(key) {
  7723  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i), key, "exists in both matchLabelKeys and labelSelector"))
  7724  		}
  7725  	}
  7726  
  7727  	return allErrs
  7728  }
  7729  
  7730  // validateLabelKeys tests that the label keys are a valid label name.
  7731  // It's intended to be used for matchLabelKeys or mismatchLabelKeys.
  7732  func validateLabelKeys(fldPath *field.Path, labelKeys []string, labelSelector *metav1.LabelSelector) field.ErrorList {
  7733  	if len(labelKeys) == 0 {
  7734  		return nil
  7735  	}
  7736  
  7737  	if labelSelector == nil {
  7738  		return field.ErrorList{field.Forbidden(fldPath, "must not be specified when labelSelector is not set")}
  7739  	}
  7740  
  7741  	var allErrs field.ErrorList
  7742  	for i, key := range labelKeys {
  7743  		allErrs = append(allErrs, unversionedvalidation.ValidateLabelName(key, fldPath.Index(i))...)
  7744  	}
  7745  
  7746  	return allErrs
  7747  }
  7748  
  7749  // ValidateServiceClusterIPsRelatedFields validates .spec.ClusterIPs,,
  7750  // .spec.IPFamilies, .spec.ipFamilyPolicy.  This is exported because it is used
  7751  // during IP init and allocation.
  7752  func ValidateServiceClusterIPsRelatedFields(service *core.Service) field.ErrorList {
  7753  	// ClusterIP, ClusterIPs, IPFamilyPolicy and IPFamilies are validated prior (all must be unset) for ExternalName service
  7754  	if service.Spec.Type == core.ServiceTypeExternalName {
  7755  		return field.ErrorList{}
  7756  	}
  7757  
  7758  	allErrs := field.ErrorList{}
  7759  	hasInvalidIPs := false
  7760  
  7761  	specPath := field.NewPath("spec")
  7762  	clusterIPsField := specPath.Child("clusterIPs")
  7763  	ipFamiliesField := specPath.Child("ipFamilies")
  7764  	ipFamilyPolicyField := specPath.Child("ipFamilyPolicy")
  7765  
  7766  	// Make sure ClusterIP and ClusterIPs are synced.  For most cases users can
  7767  	// just manage one or the other and we'll handle the rest (see PrepareFor*
  7768  	// in strategy).
  7769  	if len(service.Spec.ClusterIP) != 0 {
  7770  		// If ClusterIP is set, ClusterIPs[0] must match.
  7771  		if len(service.Spec.ClusterIPs) == 0 {
  7772  			allErrs = append(allErrs, field.Required(clusterIPsField, ""))
  7773  		} else if service.Spec.ClusterIPs[0] != service.Spec.ClusterIP {
  7774  			allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "first value must match `clusterIP`"))
  7775  		}
  7776  	} else { // ClusterIP == ""
  7777  		// If ClusterIP is not set, ClusterIPs must also be unset.
  7778  		if len(service.Spec.ClusterIPs) != 0 {
  7779  			allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "must be empty when `clusterIP` is not specified"))
  7780  		}
  7781  	}
  7782  
  7783  	// ipfamilies stand alone validation
  7784  	// must be either IPv4 or IPv6
  7785  	seen := sets.Set[core.IPFamily]{}
  7786  	for i, ipFamily := range service.Spec.IPFamilies {
  7787  		if !supportedServiceIPFamily.Has(ipFamily) {
  7788  			allErrs = append(allErrs, field.NotSupported(ipFamiliesField.Index(i), ipFamily, sets.List(supportedServiceIPFamily)))
  7789  		}
  7790  		// no duplicate check also ensures that ipfamilies is dualstacked, in any order
  7791  		if seen.Has(ipFamily) {
  7792  			allErrs = append(allErrs, field.Duplicate(ipFamiliesField.Index(i), ipFamily))
  7793  		}
  7794  		seen.Insert(ipFamily)
  7795  	}
  7796  
  7797  	// IPFamilyPolicy stand alone validation
  7798  	// note: nil is ok, defaulted in alloc check registry/core/service/*
  7799  	if service.Spec.IPFamilyPolicy != nil {
  7800  		// must have a supported value
  7801  		if !supportedServiceIPFamilyPolicy.Has(*(service.Spec.IPFamilyPolicy)) {
  7802  			allErrs = append(allErrs, field.NotSupported(ipFamilyPolicyField, service.Spec.IPFamilyPolicy, sets.List(supportedServiceIPFamilyPolicy)))
  7803  		}
  7804  	}
  7805  
  7806  	// clusterIPs stand alone validation
  7807  	// valid ips with None and empty string handling
  7808  	// duplication check is done as part of DualStackvalidation below
  7809  	for i, clusterIP := range service.Spec.ClusterIPs {
  7810  		// valid at first location only. if and only if len(clusterIPs) == 1
  7811  		if i == 0 && clusterIP == core.ClusterIPNone {
  7812  			if len(service.Spec.ClusterIPs) > 1 {
  7813  				hasInvalidIPs = true
  7814  				allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "'None' must be the first and only value"))
  7815  			}
  7816  			continue
  7817  		}
  7818  
  7819  		// is it valid ip?
  7820  		errorMessages := validation.IsValidIP(clusterIPsField.Index(i), clusterIP)
  7821  		hasInvalidIPs = (len(errorMessages) != 0) || hasInvalidIPs
  7822  		allErrs = append(allErrs, errorMessages...)
  7823  	}
  7824  
  7825  	// max two
  7826  	if len(service.Spec.ClusterIPs) > 2 {
  7827  		allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "may only hold up to 2 values"))
  7828  	}
  7829  
  7830  	// at this stage if there is an invalid ip or misplaced none/empty string
  7831  	// it will skew the error messages (bad index || dualstackness of already bad ips). so we
  7832  	// stop here if there are errors in clusterIPs validation
  7833  	if hasInvalidIPs {
  7834  		return allErrs
  7835  	}
  7836  
  7837  	// must be dual stacked ips if they are more than one ip
  7838  	if len(service.Spec.ClusterIPs) > 1 /* meaning: it does not have a None or empty string */ {
  7839  		dualStack, err := netutils.IsDualStackIPStrings(service.Spec.ClusterIPs)
  7840  		if err != nil { // though we check for that earlier. safe > sorry
  7841  			allErrs = append(allErrs, field.InternalError(clusterIPsField, fmt.Errorf("failed to check for dual stack with error:%v", err)))
  7842  		}
  7843  
  7844  		// We only support one from each IP family (i.e. max two IPs in this list).
  7845  		if !dualStack {
  7846  			allErrs = append(allErrs, field.Invalid(clusterIPsField, service.Spec.ClusterIPs, "may specify no more than one IP for each IP family"))
  7847  		}
  7848  	}
  7849  
  7850  	// match clusterIPs to their families, if they were provided
  7851  	if !isHeadlessService(service) && len(service.Spec.ClusterIPs) > 0 && len(service.Spec.IPFamilies) > 0 {
  7852  		for i, ip := range service.Spec.ClusterIPs {
  7853  			if i > (len(service.Spec.IPFamilies) - 1) {
  7854  				break // no more families to check
  7855  			}
  7856  
  7857  			// 4=>6
  7858  			if service.Spec.IPFamilies[i] == core.IPv4Protocol && netutils.IsIPv6String(ip) {
  7859  				allErrs = append(allErrs, field.Invalid(clusterIPsField.Index(i), ip, fmt.Sprintf("expected an IPv4 value as indicated by `ipFamilies[%v]`", i)))
  7860  			}
  7861  			// 6=>4
  7862  			if service.Spec.IPFamilies[i] == core.IPv6Protocol && !netutils.IsIPv6String(ip) {
  7863  				allErrs = append(allErrs, field.Invalid(clusterIPsField.Index(i), ip, fmt.Sprintf("expected an IPv6 value as indicated by `ipFamilies[%v]`", i)))
  7864  			}
  7865  		}
  7866  	}
  7867  
  7868  	return allErrs
  7869  }
  7870  
  7871  // specific validation for clusterIPs in cases of user upgrading or downgrading to/from dualstack
  7872  func validateUpgradeDowngradeClusterIPs(oldService, service *core.Service) field.ErrorList {
  7873  	allErrs := make(field.ErrorList, 0)
  7874  
  7875  	// bail out early for ExternalName
  7876  	if service.Spec.Type == core.ServiceTypeExternalName || oldService.Spec.Type == core.ServiceTypeExternalName {
  7877  		return allErrs
  7878  	}
  7879  	newIsHeadless := isHeadlessService(service)
  7880  	oldIsHeadless := isHeadlessService(oldService)
  7881  
  7882  	if oldIsHeadless && newIsHeadless {
  7883  		return allErrs
  7884  	}
  7885  
  7886  	switch {
  7887  	// no change in ClusterIP lengths
  7888  	// compare each
  7889  	case len(oldService.Spec.ClusterIPs) == len(service.Spec.ClusterIPs):
  7890  		for i, ip := range oldService.Spec.ClusterIPs {
  7891  			if ip != service.Spec.ClusterIPs[i] {
  7892  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(i), service.Spec.ClusterIPs, "may not change once set"))
  7893  			}
  7894  		}
  7895  
  7896  	// something has been released (downgraded)
  7897  	case len(oldService.Spec.ClusterIPs) > len(service.Spec.ClusterIPs):
  7898  		// primary ClusterIP has been released
  7899  		if len(service.Spec.ClusterIPs) == 0 {
  7900  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(0), service.Spec.ClusterIPs, "primary clusterIP can not be unset"))
  7901  		}
  7902  
  7903  		// test if primary clusterIP has changed
  7904  		if len(oldService.Spec.ClusterIPs) > 0 &&
  7905  			len(service.Spec.ClusterIPs) > 0 &&
  7906  			service.Spec.ClusterIPs[0] != oldService.Spec.ClusterIPs[0] {
  7907  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7908  		}
  7909  
  7910  		// test if secondary ClusterIP has been released. has this service been downgraded correctly?
  7911  		// user *must* set IPFamilyPolicy == SingleStack
  7912  		if len(service.Spec.ClusterIPs) == 1 {
  7913  			if service.Spec.IPFamilyPolicy == nil || *(service.Spec.IPFamilyPolicy) != core.IPFamilyPolicySingleStack {
  7914  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilyPolicy"), service.Spec.IPFamilyPolicy, "must be set to 'SingleStack' when releasing the secondary clusterIP"))
  7915  			}
  7916  		}
  7917  	case len(oldService.Spec.ClusterIPs) < len(service.Spec.ClusterIPs):
  7918  		// something has been added (upgraded)
  7919  		// test if primary clusterIP has changed
  7920  		if len(oldService.Spec.ClusterIPs) > 0 &&
  7921  			service.Spec.ClusterIPs[0] != oldService.Spec.ClusterIPs[0] {
  7922  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "clusterIPs").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7923  		}
  7924  		// we don't check for Policy == RequireDualStack here since, Validation/Creation func takes care of it
  7925  	}
  7926  	return allErrs
  7927  }
  7928  
  7929  // specific validation for ipFamilies in cases of user upgrading or downgrading to/from dualstack
  7930  func validateUpgradeDowngradeIPFamilies(oldService, service *core.Service) field.ErrorList {
  7931  	allErrs := make(field.ErrorList, 0)
  7932  	// bail out early for ExternalName
  7933  	if service.Spec.Type == core.ServiceTypeExternalName || oldService.Spec.Type == core.ServiceTypeExternalName {
  7934  		return allErrs
  7935  	}
  7936  
  7937  	oldIsHeadless := isHeadlessService(oldService)
  7938  	newIsHeadless := isHeadlessService(service)
  7939  
  7940  	// if changed to/from headless, then bail out
  7941  	if newIsHeadless != oldIsHeadless {
  7942  		return allErrs
  7943  	}
  7944  	// headless can change families
  7945  	if newIsHeadless {
  7946  		return allErrs
  7947  	}
  7948  
  7949  	switch {
  7950  	case len(oldService.Spec.IPFamilies) == len(service.Spec.IPFamilies):
  7951  		// no change in ClusterIP lengths
  7952  		// compare each
  7953  
  7954  		for i, ip := range oldService.Spec.IPFamilies {
  7955  			if ip != service.Spec.IPFamilies[i] {
  7956  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.IPFamilies, "may not change once set"))
  7957  			}
  7958  		}
  7959  
  7960  	case len(oldService.Spec.IPFamilies) > len(service.Spec.IPFamilies):
  7961  		// something has been released (downgraded)
  7962  
  7963  		// test if primary ipfamily has been released
  7964  		if len(service.Spec.ClusterIPs) == 0 {
  7965  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.IPFamilies, "primary ipFamily can not be unset"))
  7966  		}
  7967  
  7968  		// test if primary ipFamily has changed
  7969  		if len(service.Spec.IPFamilies) > 0 &&
  7970  			service.Spec.IPFamilies[0] != oldService.Spec.IPFamilies[0] {
  7971  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7972  		}
  7973  
  7974  		// test if secondary IPFamily has been released. has this service been downgraded correctly?
  7975  		// user *must* set IPFamilyPolicy == SingleStack
  7976  		if len(service.Spec.IPFamilies) == 1 {
  7977  			if service.Spec.IPFamilyPolicy == nil || *(service.Spec.IPFamilyPolicy) != core.IPFamilyPolicySingleStack {
  7978  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilyPolicy"), service.Spec.IPFamilyPolicy, "must be set to 'SingleStack' when releasing the secondary ipFamily"))
  7979  			}
  7980  		}
  7981  	case len(oldService.Spec.IPFamilies) < len(service.Spec.IPFamilies):
  7982  		// something has been added (upgraded)
  7983  
  7984  		// test if primary ipFamily has changed
  7985  		if len(oldService.Spec.IPFamilies) > 0 &&
  7986  			len(service.Spec.IPFamilies) > 0 &&
  7987  			service.Spec.IPFamilies[0] != oldService.Spec.IPFamilies[0] {
  7988  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "ipFamilies").Index(0), service.Spec.ClusterIPs, "may not change once set"))
  7989  		}
  7990  		// we don't check for Policy == RequireDualStack here since, Validation/Creation func takes care of it
  7991  	}
  7992  	return allErrs
  7993  }
  7994  
  7995  func isHeadlessService(service *core.Service) bool {
  7996  	return service != nil &&
  7997  		len(service.Spec.ClusterIPs) == 1 &&
  7998  		service.Spec.ClusterIPs[0] == core.ClusterIPNone
  7999  }
  8000  
  8001  // validateLoadBalancerClassField validation for loadBalancerClass
  8002  func validateLoadBalancerClassField(oldService, service *core.Service) field.ErrorList {
  8003  	allErrs := make(field.ErrorList, 0)
  8004  	if oldService != nil {
  8005  		// validate update op
  8006  		if isTypeLoadBalancer(oldService) && isTypeLoadBalancer(service) {
  8007  			// old and new are both LoadBalancer
  8008  			if !sameLoadBalancerClass(oldService, service) {
  8009  				// can't change loadBalancerClass
  8010  				allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "loadBalancerClass"), service.Spec.LoadBalancerClass, "may not change once set"))
  8011  			}
  8012  		}
  8013  	}
  8014  
  8015  	if isTypeLoadBalancer(service) {
  8016  		// check LoadBalancerClass format
  8017  		if service.Spec.LoadBalancerClass != nil {
  8018  			allErrs = append(allErrs, ValidateQualifiedName(*service.Spec.LoadBalancerClass, field.NewPath("spec", "loadBalancerClass"))...)
  8019  		}
  8020  	} else {
  8021  		// check if LoadBalancerClass set for non LoadBalancer type of service
  8022  		if service.Spec.LoadBalancerClass != nil {
  8023  			allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "loadBalancerClass"), "may only be used when `type` is 'LoadBalancer'"))
  8024  		}
  8025  	}
  8026  	return allErrs
  8027  }
  8028  
  8029  // isTypeLoadBalancer tests service type is loadBalancer or not
  8030  func isTypeLoadBalancer(service *core.Service) bool {
  8031  	return service.Spec.Type == core.ServiceTypeLoadBalancer
  8032  }
  8033  
  8034  // sameLoadBalancerClass check two services have the same loadBalancerClass or not
  8035  func sameLoadBalancerClass(oldService, service *core.Service) bool {
  8036  	if oldService.Spec.LoadBalancerClass == nil && service.Spec.LoadBalancerClass == nil {
  8037  		return true
  8038  	}
  8039  	if oldService.Spec.LoadBalancerClass == nil || service.Spec.LoadBalancerClass == nil {
  8040  		return false
  8041  	}
  8042  	return *oldService.Spec.LoadBalancerClass == *service.Spec.LoadBalancerClass
  8043  }
  8044  
  8045  func ValidatePodAffinityTermSelector(podAffinityTerm core.PodAffinityTerm, allowInvalidLabelValueInSelector bool, fldPath *field.Path) field.ErrorList {
  8046  	var allErrs field.ErrorList
  8047  	labelSelectorValidationOptions := unversionedvalidation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: allowInvalidLabelValueInSelector}
  8048  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.LabelSelector, labelSelectorValidationOptions, fldPath.Child("labelSelector"))...)
  8049  	allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.NamespaceSelector, labelSelectorValidationOptions, fldPath.Child("namespaceSelector"))...)
  8050  	return allErrs
  8051  }
  8052  
  8053  var betaToGALabel = map[string]string{
  8054  	v1.LabelFailureDomainBetaZone:   v1.LabelTopologyZone,
  8055  	v1.LabelFailureDomainBetaRegion: v1.LabelTopologyRegion,
  8056  	kubeletapis.LabelOS:             v1.LabelOSStable,
  8057  	kubeletapis.LabelArch:           v1.LabelArchStable,
  8058  	v1.LabelInstanceType:            v1.LabelInstanceTypeStable,
  8059  }
  8060  
  8061  var (
  8062  	maskNodeSelectorLabelChangeEqualities     conversion.Equalities
  8063  	initMaskNodeSelectorLabelChangeEqualities sync.Once
  8064  )
  8065  
  8066  func getMaskNodeSelectorLabelChangeEqualities() conversion.Equalities {
  8067  	initMaskNodeSelectorLabelChangeEqualities.Do(func() {
  8068  		var eqs = apiequality.Semantic.Copy()
  8069  		err := eqs.AddFunc(
  8070  			func(newReq, oldReq core.NodeSelectorRequirement) bool {
  8071  				// allow newReq to change to a GA key
  8072  				if oldReq.Key != newReq.Key && betaToGALabel[oldReq.Key] == newReq.Key {
  8073  					oldReq.Key = newReq.Key // +k8s:verify-mutation:reason=clone
  8074  				}
  8075  				return apiequality.Semantic.DeepEqual(newReq, oldReq)
  8076  			},
  8077  		)
  8078  		if err != nil {
  8079  			panic(fmt.Errorf("failed to instantiate semantic equalities: %w", err))
  8080  		}
  8081  		maskNodeSelectorLabelChangeEqualities = eqs
  8082  	})
  8083  	return maskNodeSelectorLabelChangeEqualities
  8084  }
  8085  
  8086  func validatePvNodeAffinity(newPvNodeAffinity, oldPvNodeAffinity *core.VolumeNodeAffinity, fldPath *field.Path) field.ErrorList {
  8087  	var allErrs field.ErrorList
  8088  	if !getMaskNodeSelectorLabelChangeEqualities().DeepEqual(newPvNodeAffinity, oldPvNodeAffinity) {
  8089  		allErrs = append(allErrs, field.Invalid(fldPath, newPvNodeAffinity, fieldImmutableErrorMsg+", except for updating from beta label to GA"))
  8090  	}
  8091  	return allErrs
  8092  }
  8093  
  8094  func validateNodeSelectorMutation(fldPath *field.Path, newNodeSelector, oldNodeSelector map[string]string) field.ErrorList {
  8095  	var allErrs field.ErrorList
  8096  
  8097  	// Validate no existing node selectors were deleted or mutated.
  8098  	for k, v1 := range oldNodeSelector {
  8099  		if v2, ok := newNodeSelector[k]; !ok || v1 != v2 {
  8100  			allErrs = append(allErrs, field.Invalid(fldPath, newNodeSelector, "only additions to spec.nodeSelector are allowed (no mutations or deletions)"))
  8101  			return allErrs
  8102  		}
  8103  	}
  8104  	return allErrs
  8105  }
  8106  
  8107  func validateNodeAffinityMutation(nodeAffinityPath *field.Path, newNodeAffinity, oldNodeAffinity *core.NodeAffinity) field.ErrorList {
  8108  	var allErrs field.ErrorList
  8109  	// If old node affinity was nil, anything can be set.
  8110  	if oldNodeAffinity == nil || oldNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
  8111  		return allErrs
  8112  	}
  8113  
  8114  	oldTerms := oldNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
  8115  	var newTerms []core.NodeSelectorTerm
  8116  	if newNodeAffinity != nil && newNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
  8117  		newTerms = newNodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
  8118  	}
  8119  
  8120  	// If there are no old terms, we can set the new terms to anything.
  8121  	// If there are old terms, we cannot add any new ones.
  8122  	if len(oldTerms) > 0 && len(oldTerms) != len(newTerms) {
  8123  		return append(allErrs, field.Invalid(nodeAffinityPath.Child("requiredDuringSchedulingIgnoredDuringExecution").Child("nodeSelectorTerms"), newTerms, "no additions/deletions to non-empty NodeSelectorTerms list are allowed"))
  8124  	}
  8125  
  8126  	// For requiredDuringSchedulingIgnoredDuringExecution, if old NodeSelectorTerms
  8127  	// was empty, anything can be set. If non-empty, only additions of NodeSelectorRequirements
  8128  	// to matchExpressions or fieldExpressions are allowed.
  8129  	for i := range oldTerms {
  8130  		if !validateNodeSelectorTermHasOnlyAdditions(newTerms[i], oldTerms[i]) {
  8131  			allErrs = append(allErrs, field.Invalid(nodeAffinityPath.Child("requiredDuringSchedulingIgnoredDuringExecution").Child("nodeSelectorTerms").Index(i), newTerms[i], "only additions are allowed (no mutations or deletions)"))
  8132  		}
  8133  	}
  8134  	return allErrs
  8135  }
  8136  
  8137  func validateNodeSelectorTermHasOnlyAdditions(newTerm, oldTerm core.NodeSelectorTerm) bool {
  8138  	if len(oldTerm.MatchExpressions) == 0 && len(oldTerm.MatchFields) == 0 {
  8139  		if len(newTerm.MatchExpressions) > 0 || len(newTerm.MatchFields) > 0 {
  8140  			return false
  8141  		}
  8142  	}
  8143  
  8144  	// Validate MatchExpressions only has additions (no deletions or mutations)
  8145  	if l := len(oldTerm.MatchExpressions); l > 0 {
  8146  		if len(newTerm.MatchExpressions) < l {
  8147  			return false
  8148  		}
  8149  		if !apiequality.Semantic.DeepEqual(newTerm.MatchExpressions[:l], oldTerm.MatchExpressions) {
  8150  			return false
  8151  		}
  8152  	}
  8153  	// Validate MatchFields only has additions (no deletions or mutations)
  8154  	if l := len(oldTerm.MatchFields); l > 0 {
  8155  		if len(newTerm.MatchFields) < l {
  8156  			return false
  8157  		}
  8158  		if !apiequality.Semantic.DeepEqual(newTerm.MatchFields[:l], oldTerm.MatchFields) {
  8159  			return false
  8160  		}
  8161  	}
  8162  	return true
  8163  }
  8164  

View as plain text