...

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

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

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package validation
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"time"
    24  
    25  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    26  	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
    27  	metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  	"k8s.io/apimachinery/pkg/util/validation/field"
    30  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    31  	api "k8s.io/kubernetes/pkg/apis/core"
    32  	"k8s.io/kubernetes/pkg/apis/core/helper"
    33  	apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
    34  	"k8s.io/kubernetes/pkg/apis/storage"
    35  	"k8s.io/kubernetes/pkg/features"
    36  )
    37  
    38  const (
    39  	maxProvisionerParameterSize = 256 * (1 << 10) // 256 kB
    40  	maxProvisionerParameterLen  = 512
    41  
    42  	maxAttachedVolumeMetadataSize = 256 * (1 << 10) // 256 kB
    43  	maxVolumeErrorMessageSize     = 1024
    44  
    45  	csiNodeIDMaxLength       = 192
    46  	csiNodeIDLongerMaxLength = 256
    47  )
    48  
    49  // CSINodeValidationOptions contains the validation options for validating CSINode
    50  type CSINodeValidationOptions struct {
    51  	AllowLongNodeID bool
    52  }
    53  
    54  // ValidateStorageClass validates a StorageClass.
    55  func ValidateStorageClass(storageClass *storage.StorageClass) field.ErrorList {
    56  	allErrs := apivalidation.ValidateObjectMeta(&storageClass.ObjectMeta, false, apivalidation.ValidateClassName, field.NewPath("metadata"))
    57  	allErrs = append(allErrs, validateProvisioner(storageClass.Provisioner, field.NewPath("provisioner"))...)
    58  	allErrs = append(allErrs, validateParameters(storageClass.Parameters, true, field.NewPath("parameters"))...)
    59  	allErrs = append(allErrs, validateReclaimPolicy(storageClass.ReclaimPolicy, field.NewPath("reclaimPolicy"))...)
    60  	allErrs = append(allErrs, validateVolumeBindingMode(storageClass.VolumeBindingMode, field.NewPath("volumeBindingMode"))...)
    61  	allErrs = append(allErrs, validateAllowedTopologies(storageClass.AllowedTopologies, field.NewPath("allowedTopologies"))...)
    62  
    63  	return allErrs
    64  }
    65  
    66  // ValidateStorageClassUpdate tests if an update to StorageClass is valid.
    67  func ValidateStorageClassUpdate(storageClass, oldStorageClass *storage.StorageClass) field.ErrorList {
    68  	allErrs := apivalidation.ValidateObjectMetaUpdate(&storageClass.ObjectMeta, &oldStorageClass.ObjectMeta, field.NewPath("metadata"))
    69  	if !reflect.DeepEqual(oldStorageClass.Parameters, storageClass.Parameters) {
    70  		allErrs = append(allErrs, field.Forbidden(field.NewPath("parameters"), "updates to parameters are forbidden."))
    71  	}
    72  
    73  	if storageClass.Provisioner != oldStorageClass.Provisioner {
    74  		allErrs = append(allErrs, field.Forbidden(field.NewPath("provisioner"), "updates to provisioner are forbidden."))
    75  	}
    76  
    77  	if *storageClass.ReclaimPolicy != *oldStorageClass.ReclaimPolicy {
    78  		allErrs = append(allErrs, field.Forbidden(field.NewPath("reclaimPolicy"), "updates to reclaimPolicy are forbidden."))
    79  	}
    80  
    81  	allErrs = append(allErrs, apivalidation.ValidateImmutableField(storageClass.VolumeBindingMode, oldStorageClass.VolumeBindingMode, field.NewPath("volumeBindingMode"))...)
    82  	return allErrs
    83  }
    84  
    85  // validateProvisioner tests if provisioner is a valid qualified name.
    86  func validateProvisioner(provisioner string, fldPath *field.Path) field.ErrorList {
    87  	allErrs := field.ErrorList{}
    88  	if len(provisioner) == 0 {
    89  		allErrs = append(allErrs, field.Required(fldPath, provisioner))
    90  	}
    91  	if len(provisioner) > 0 {
    92  		allErrs = append(allErrs, apivalidation.ValidateQualifiedName(strings.ToLower(provisioner), fldPath)...)
    93  	}
    94  	return allErrs
    95  }
    96  
    97  // validateParameters tests that keys are qualified names and that provisionerParameter are < 256kB.
    98  func validateParameters(params map[string]string, allowEmpty bool, fldPath *field.Path) field.ErrorList {
    99  	var totalSize int64
   100  	allErrs := field.ErrorList{}
   101  
   102  	if len(params) > maxProvisionerParameterLen {
   103  		allErrs = append(allErrs, field.TooLong(fldPath, "Provisioner Parameters exceeded max allowed", maxProvisionerParameterLen))
   104  		return allErrs
   105  	}
   106  
   107  	for k, v := range params {
   108  		if len(k) < 1 {
   109  			allErrs = append(allErrs, field.Invalid(fldPath, k, "field can not be empty."))
   110  		}
   111  		totalSize += (int64)(len(k)) + (int64)(len(v))
   112  	}
   113  
   114  	if totalSize > maxProvisionerParameterSize {
   115  		allErrs = append(allErrs, field.TooLong(fldPath, "", maxProvisionerParameterSize))
   116  	}
   117  
   118  	if !allowEmpty && len(params) == 0 {
   119  		allErrs = append(allErrs, field.Required(fldPath, "must contain at least one key/value pair"))
   120  	}
   121  	return allErrs
   122  }
   123  
   124  var supportedReclaimPolicy = sets.NewString(string(api.PersistentVolumeReclaimDelete), string(api.PersistentVolumeReclaimRetain))
   125  
   126  // validateReclaimPolicy tests that the reclaim policy is one of the supported. It is up to the volume plugin to reject
   127  // provisioning for storage classes with impossible reclaim policies, e.g. EBS is not Recyclable
   128  func validateReclaimPolicy(reclaimPolicy *api.PersistentVolumeReclaimPolicy, fldPath *field.Path) field.ErrorList {
   129  	allErrs := field.ErrorList{}
   130  	if len(string(*reclaimPolicy)) > 0 {
   131  		if !supportedReclaimPolicy.Has(string(*reclaimPolicy)) {
   132  			allErrs = append(allErrs, field.NotSupported(fldPath, reclaimPolicy, supportedReclaimPolicy.List()))
   133  		}
   134  	}
   135  	return allErrs
   136  }
   137  
   138  // ValidateVolumeAttachment validates a VolumeAttachment. This function is common for v1 and v1beta1 objects,
   139  func ValidateVolumeAttachment(volumeAttachment *storage.VolumeAttachment) field.ErrorList {
   140  	allErrs := apivalidation.ValidateObjectMeta(&volumeAttachment.ObjectMeta, false, apivalidation.ValidateClassName, field.NewPath("metadata"))
   141  	allErrs = append(allErrs, validateVolumeAttachmentSpec(&volumeAttachment.Spec, field.NewPath("spec"))...)
   142  	allErrs = append(allErrs, validateVolumeAttachmentStatus(&volumeAttachment.Status, field.NewPath("status"))...)
   143  	return allErrs
   144  }
   145  
   146  // ValidateVolumeAttachmentV1 validates a v1/VolumeAttachment. It contains only extra checks missing in
   147  // ValidateVolumeAttachment.
   148  func ValidateVolumeAttachmentV1(volumeAttachment *storage.VolumeAttachment) field.ErrorList {
   149  	allErrs := apivalidation.ValidateCSIDriverName(volumeAttachment.Spec.Attacher, field.NewPath("spec.attacher"))
   150  
   151  	if volumeAttachment.Spec.Source.PersistentVolumeName != nil {
   152  		pvName := *volumeAttachment.Spec.Source.PersistentVolumeName
   153  		for _, msg := range apivalidation.ValidatePersistentVolumeName(pvName, false) {
   154  			allErrs = append(allErrs, field.Invalid(field.NewPath("spec.source.persistentVolumeName"), pvName, msg))
   155  		}
   156  	}
   157  	return allErrs
   158  }
   159  
   160  // ValidateVolumeAttachmentSpec tests that the specified VolumeAttachmentSpec
   161  // has valid data.
   162  func validateVolumeAttachmentSpec(
   163  	spec *storage.VolumeAttachmentSpec, fldPath *field.Path) field.ErrorList {
   164  	allErrs := field.ErrorList{}
   165  	allErrs = append(allErrs, validateAttacher(spec.Attacher, fldPath.Child("attacher"))...)
   166  	allErrs = append(allErrs, validateVolumeAttachmentSource(&spec.Source, fldPath.Child("source"))...)
   167  	allErrs = append(allErrs, validateNodeName(spec.NodeName, fldPath.Child("nodeName"))...)
   168  	return allErrs
   169  }
   170  
   171  // validateAttacher tests if attacher is a valid qualified name.
   172  func validateAttacher(attacher string, fldPath *field.Path) field.ErrorList {
   173  	allErrs := field.ErrorList{}
   174  	if len(attacher) == 0 {
   175  		allErrs = append(allErrs, field.Required(fldPath, attacher))
   176  	}
   177  	return allErrs
   178  }
   179  
   180  // validateSource tests if the source is valid for VolumeAttachment.
   181  func validateVolumeAttachmentSource(source *storage.VolumeAttachmentSource, fldPath *field.Path) field.ErrorList {
   182  	allErrs := field.ErrorList{}
   183  	switch {
   184  	case source.InlineVolumeSpec == nil && source.PersistentVolumeName == nil:
   185  		allErrs = append(allErrs, field.Required(fldPath, "must specify exactly one of inlineVolumeSpec and persistentVolumeName"))
   186  	case source.InlineVolumeSpec != nil && source.PersistentVolumeName != nil:
   187  		allErrs = append(allErrs, field.Forbidden(fldPath, "must specify exactly one of inlineVolumeSpec and persistentVolumeName"))
   188  	case source.PersistentVolumeName != nil:
   189  		if len(*source.PersistentVolumeName) == 0 {
   190  			// Invalid err
   191  			allErrs = append(allErrs, field.Required(fldPath.Child("persistentVolumeName"), "must specify non empty persistentVolumeName"))
   192  		}
   193  	case source.InlineVolumeSpec != nil:
   194  		opts := apivalidation.PersistentVolumeSpecValidationOptions{}
   195  		allErrs = append(allErrs, apivalidation.ValidatePersistentVolumeSpec(source.InlineVolumeSpec, "", true, fldPath.Child("inlineVolumeSpec"), opts)...)
   196  	}
   197  	return allErrs
   198  }
   199  
   200  // validateNodeName tests if the nodeName is valid for VolumeAttachment.
   201  func validateNodeName(nodeName string, fldPath *field.Path) field.ErrorList {
   202  	allErrs := field.ErrorList{}
   203  	for _, msg := range apivalidation.ValidateNodeName(nodeName, false /* prefix */) {
   204  		allErrs = append(allErrs, field.Invalid(fldPath, nodeName, msg))
   205  	}
   206  	return allErrs
   207  }
   208  
   209  // validaVolumeAttachmentStatus tests if volumeAttachmentStatus is valid.
   210  func validateVolumeAttachmentStatus(status *storage.VolumeAttachmentStatus, fldPath *field.Path) field.ErrorList {
   211  	allErrs := field.ErrorList{}
   212  	allErrs = append(allErrs, validateAttachmentMetadata(status.AttachmentMetadata, fldPath.Child("attachmentMetadata"))...)
   213  	allErrs = append(allErrs, validateVolumeError(status.AttachError, fldPath.Child("attachError"))...)
   214  	allErrs = append(allErrs, validateVolumeError(status.DetachError, fldPath.Child("detachError"))...)
   215  	return allErrs
   216  }
   217  
   218  func validateAttachmentMetadata(metadata map[string]string, fldPath *field.Path) field.ErrorList {
   219  	allErrs := field.ErrorList{}
   220  
   221  	var size int64
   222  	for k, v := range metadata {
   223  		size += (int64)(len(k)) + (int64)(len(v))
   224  	}
   225  	if size > maxAttachedVolumeMetadataSize {
   226  		allErrs = append(allErrs, field.TooLong(fldPath, metadata, maxAttachedVolumeMetadataSize))
   227  	}
   228  	return allErrs
   229  }
   230  
   231  func validateVolumeError(e *storage.VolumeError, fldPath *field.Path) field.ErrorList {
   232  	allErrs := field.ErrorList{}
   233  
   234  	if e == nil {
   235  		return allErrs
   236  	}
   237  	if len(e.Message) > maxVolumeErrorMessageSize {
   238  		allErrs = append(allErrs, field.TooLong(fldPath.Child("message"), e.Message, maxAttachedVolumeMetadataSize))
   239  	}
   240  	return allErrs
   241  }
   242  
   243  // ValidateVolumeAttachmentUpdate validates a VolumeAttachment.
   244  func ValidateVolumeAttachmentUpdate(new, old *storage.VolumeAttachment) field.ErrorList {
   245  	allErrs := ValidateVolumeAttachment(new)
   246  
   247  	// Spec is read-only
   248  	// If this ever relaxes in the future, make sure to increment the Generation number in PrepareForUpdate
   249  	if !apiequality.Semantic.DeepEqual(old.Spec, new.Spec) {
   250  		allErrs = append(allErrs, field.Invalid(field.NewPath("spec"), new.Spec, "field is immutable"))
   251  	}
   252  	return allErrs
   253  }
   254  
   255  var supportedVolumeBindingModes = sets.NewString(string(storage.VolumeBindingImmediate), string(storage.VolumeBindingWaitForFirstConsumer))
   256  
   257  // validateVolumeBindingMode tests that VolumeBindingMode specifies valid values.
   258  func validateVolumeBindingMode(mode *storage.VolumeBindingMode, fldPath *field.Path) field.ErrorList {
   259  	allErrs := field.ErrorList{}
   260  	if mode == nil {
   261  		allErrs = append(allErrs, field.Required(fldPath, ""))
   262  	} else if !supportedVolumeBindingModes.Has(string(*mode)) {
   263  		allErrs = append(allErrs, field.NotSupported(fldPath, mode, supportedVolumeBindingModes.List()))
   264  	}
   265  
   266  	return allErrs
   267  }
   268  
   269  // validateAllowedTopology tests that AllowedTopologies specifies valid values.
   270  func validateAllowedTopologies(topologies []api.TopologySelectorTerm, fldPath *field.Path) field.ErrorList {
   271  	allErrs := field.ErrorList{}
   272  
   273  	if len(topologies) == 0 {
   274  		return allErrs
   275  	}
   276  
   277  	rawTopologies := make([]map[string]sets.Set[string], len(topologies))
   278  	for i, term := range topologies {
   279  		idxPath := fldPath.Index(i)
   280  		exprMap, termErrs := apivalidation.ValidateTopologySelectorTerm(term, fldPath.Index(i))
   281  		allErrs = append(allErrs, termErrs...)
   282  
   283  		// TODO (verult) consider improving runtime
   284  		for _, t := range rawTopologies {
   285  			if helper.Semantic.DeepEqual(exprMap, t) {
   286  				allErrs = append(allErrs, field.Duplicate(idxPath.Child("matchLabelExpressions"), ""))
   287  			}
   288  		}
   289  
   290  		rawTopologies = append(rawTopologies, exprMap)
   291  	}
   292  
   293  	return allErrs
   294  }
   295  
   296  // ValidateCSINode validates a CSINode.
   297  func ValidateCSINode(csiNode *storage.CSINode, validationOpts CSINodeValidationOptions) field.ErrorList {
   298  	allErrs := apivalidation.ValidateObjectMeta(&csiNode.ObjectMeta, false, apivalidation.ValidateNodeName, field.NewPath("metadata"))
   299  	allErrs = append(allErrs, validateCSINodeSpec(&csiNode.Spec, field.NewPath("spec"), validationOpts)...)
   300  	return allErrs
   301  }
   302  
   303  // ValidateCSINodeUpdate validates a CSINode.
   304  func ValidateCSINodeUpdate(new, old *storage.CSINode, validationOpts CSINodeValidationOptions) field.ErrorList {
   305  	allErrs := ValidateCSINode(new, validationOpts)
   306  
   307  	// Validate modifying fields inside an existing CSINodeDriver entry is not allowed
   308  	for _, oldDriver := range old.Spec.Drivers {
   309  		for _, newDriver := range new.Spec.Drivers {
   310  			if oldDriver.Name == newDriver.Name {
   311  				if !apiequality.Semantic.DeepEqual(oldDriver, newDriver) {
   312  					allErrs = append(allErrs, field.Invalid(field.NewPath("CSINodeDriver"), newDriver, "field is immutable"))
   313  				}
   314  			}
   315  		}
   316  	}
   317  
   318  	return allErrs
   319  }
   320  
   321  // ValidateCSINodeSpec tests that the specified CSINodeSpec has valid data.
   322  func validateCSINodeSpec(
   323  	spec *storage.CSINodeSpec, fldPath *field.Path, validationOpts CSINodeValidationOptions) field.ErrorList {
   324  	allErrs := field.ErrorList{}
   325  	allErrs = append(allErrs, validateCSINodeDrivers(spec.Drivers, fldPath.Child("drivers"), validationOpts)...)
   326  	return allErrs
   327  }
   328  
   329  // ValidateCSINodeDrivers tests that the specified CSINodeDrivers have valid data.
   330  func validateCSINodeDrivers(drivers []storage.CSINodeDriver, fldPath *field.Path, validationOpts CSINodeValidationOptions) field.ErrorList {
   331  	allErrs := field.ErrorList{}
   332  	driverNamesInSpecs := sets.New[string]()
   333  	for i, driver := range drivers {
   334  		idxPath := fldPath.Index(i)
   335  		allErrs = append(allErrs, validateCSINodeDriver(driver, driverNamesInSpecs, idxPath, validationOpts)...)
   336  	}
   337  
   338  	return allErrs
   339  }
   340  
   341  // validateCSINodeDriverNodeID tests if Name in CSINodeDriver is a valid node id.
   342  func validateCSINodeDriverNodeID(nodeID string, fldPath *field.Path, validationOpts CSINodeValidationOptions) field.ErrorList {
   343  	allErrs := field.ErrorList{}
   344  
   345  	// nodeID is always required
   346  	if len(nodeID) == 0 {
   347  		allErrs = append(allErrs, field.Required(fldPath, nodeID))
   348  	}
   349  	maxLength := csiNodeIDMaxLength
   350  	if validationOpts.AllowLongNodeID {
   351  		maxLength = csiNodeIDLongerMaxLength
   352  	}
   353  	if len(nodeID) > maxLength {
   354  		allErrs = append(allErrs, field.Invalid(fldPath, nodeID, fmt.Sprintf("must be %d characters or less", maxLength)))
   355  	}
   356  	return allErrs
   357  }
   358  
   359  // validateCSINodeDriverAllocatable tests if Allocatable in CSINodeDriver has valid volume limits.
   360  func validateCSINodeDriverAllocatable(a *storage.VolumeNodeResources, fldPath *field.Path) field.ErrorList {
   361  	allErrs := field.ErrorList{}
   362  
   363  	if a == nil || a.Count == nil {
   364  		return allErrs
   365  	}
   366  
   367  	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*a.Count), fldPath.Child("count"))...)
   368  	return allErrs
   369  }
   370  
   371  // validateCSINodeDriver tests if CSINodeDriver has valid entries
   372  func validateCSINodeDriver(driver storage.CSINodeDriver, driverNamesInSpecs sets.Set[string], fldPath *field.Path,
   373  	validationOpts CSINodeValidationOptions) field.ErrorList {
   374  	allErrs := field.ErrorList{}
   375  
   376  	allErrs = append(allErrs, apivalidation.ValidateCSIDriverName(driver.Name, fldPath.Child("name"))...)
   377  	allErrs = append(allErrs, validateCSINodeDriverNodeID(driver.NodeID, fldPath.Child("nodeID"), validationOpts)...)
   378  	allErrs = append(allErrs, validateCSINodeDriverAllocatable(driver.Allocatable, fldPath.Child("allocatable"))...)
   379  
   380  	// check for duplicate entries for the same driver in specs
   381  	if driverNamesInSpecs.Has(driver.Name) {
   382  		allErrs = append(allErrs, field.Duplicate(fldPath.Child("name"), driver.Name))
   383  	}
   384  	driverNamesInSpecs.Insert(driver.Name)
   385  	topoKeys := sets.New[string]()
   386  	for _, key := range driver.TopologyKeys {
   387  		if len(key) == 0 {
   388  			allErrs = append(allErrs, field.Required(fldPath, key))
   389  		}
   390  
   391  		if topoKeys.Has(key) {
   392  			allErrs = append(allErrs, field.Duplicate(fldPath, key))
   393  		}
   394  		topoKeys.Insert(key)
   395  
   396  		allErrs = append(allErrs, apivalidation.ValidateQualifiedName(key, fldPath)...)
   397  	}
   398  
   399  	return allErrs
   400  }
   401  
   402  // ValidateCSIDriverName checks that a name is appropriate for a
   403  // CSIDriver object.
   404  var ValidateCSIDriverName = apimachineryvalidation.NameIsDNSSubdomain
   405  
   406  // ValidateCSIDriver validates a CSIDriver.
   407  func ValidateCSIDriver(csiDriver *storage.CSIDriver) field.ErrorList {
   408  	allErrs := apivalidation.ValidateObjectMeta(&csiDriver.ObjectMeta, false, ValidateCSIDriverName, field.NewPath("metadata"))
   409  
   410  	allErrs = append(allErrs, validateCSIDriverSpec(&csiDriver.Spec, field.NewPath("spec"))...)
   411  	return allErrs
   412  }
   413  
   414  // ValidateCSIDriverUpdate validates a CSIDriver.
   415  func ValidateCSIDriverUpdate(new, old *storage.CSIDriver) field.ErrorList {
   416  	allErrs := apivalidation.ValidateObjectMetaUpdate(&new.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
   417  	allErrs = append(allErrs, validateCSIDriverSpec(&new.Spec, field.NewPath("spec"))...)
   418  
   419  	// immutable fields should not be mutated.
   420  	allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(new.Spec.AttachRequired, old.Spec.AttachRequired, field.NewPath("spec", "attachedRequired"))...)
   421  	allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(new.Spec.VolumeLifecycleModes, old.Spec.VolumeLifecycleModes, field.NewPath("spec", "volumeLifecycleModes"))...)
   422  
   423  	return allErrs
   424  }
   425  
   426  // ValidateCSIDriverSpec tests that the specified CSIDriverSpec
   427  // has valid data.
   428  func validateCSIDriverSpec(
   429  	spec *storage.CSIDriverSpec, fldPath *field.Path) field.ErrorList {
   430  	allErrs := field.ErrorList{}
   431  	allErrs = append(allErrs, validateAttachRequired(spec.AttachRequired, fldPath.Child("attachedRequired"))...)
   432  	allErrs = append(allErrs, validatePodInfoOnMount(spec.PodInfoOnMount, fldPath.Child("podInfoOnMount"))...)
   433  	allErrs = append(allErrs, validateStorageCapacity(spec.StorageCapacity, fldPath.Child("storageCapacity"))...)
   434  	allErrs = append(allErrs, validateFSGroupPolicy(spec.FSGroupPolicy, fldPath.Child("fsGroupPolicy"))...)
   435  	allErrs = append(allErrs, validateTokenRequests(spec.TokenRequests, fldPath.Child("tokenRequests"))...)
   436  	allErrs = append(allErrs, validateVolumeLifecycleModes(spec.VolumeLifecycleModes, fldPath.Child("volumeLifecycleModes"))...)
   437  	allErrs = append(allErrs, validateSELinuxMount(spec.SELinuxMount, fldPath.Child("seLinuxMount"))...)
   438  	return allErrs
   439  }
   440  
   441  // validateAttachRequired tests if attachRequired is set for CSIDriver.
   442  func validateAttachRequired(attachRequired *bool, fldPath *field.Path) field.ErrorList {
   443  	allErrs := field.ErrorList{}
   444  	if attachRequired == nil {
   445  		allErrs = append(allErrs, field.Required(fldPath, ""))
   446  	}
   447  
   448  	return allErrs
   449  }
   450  
   451  // validatePodInfoOnMount tests if podInfoOnMount is set for CSIDriver.
   452  func validatePodInfoOnMount(podInfoOnMount *bool, fldPath *field.Path) field.ErrorList {
   453  	allErrs := field.ErrorList{}
   454  	if podInfoOnMount == nil {
   455  		allErrs = append(allErrs, field.Required(fldPath, ""))
   456  	}
   457  
   458  	return allErrs
   459  }
   460  
   461  // validateStorageCapacity tests if storageCapacity is set for CSIDriver.
   462  func validateStorageCapacity(storageCapacity *bool, fldPath *field.Path) field.ErrorList {
   463  	allErrs := field.ErrorList{}
   464  	if storageCapacity == nil {
   465  		allErrs = append(allErrs, field.Required(fldPath, ""))
   466  	}
   467  
   468  	return allErrs
   469  }
   470  
   471  var supportedFSGroupPolicy = sets.NewString(string(storage.ReadWriteOnceWithFSTypeFSGroupPolicy), string(storage.FileFSGroupPolicy), string(storage.NoneFSGroupPolicy))
   472  
   473  // validateFSGroupPolicy tests if FSGroupPolicy contains an appropriate value.
   474  func validateFSGroupPolicy(fsGroupPolicy *storage.FSGroupPolicy, fldPath *field.Path) field.ErrorList {
   475  	allErrs := field.ErrorList{}
   476  	if fsGroupPolicy == nil {
   477  		// This is not a required field, so if nothing is provided simply return
   478  		return allErrs
   479  	}
   480  
   481  	if !supportedFSGroupPolicy.Has(string(*fsGroupPolicy)) {
   482  		allErrs = append(allErrs, field.NotSupported(fldPath, fsGroupPolicy, supportedFSGroupPolicy.List()))
   483  	}
   484  
   485  	return allErrs
   486  }
   487  
   488  // validateTokenRequests tests if the Audience in each TokenRequest are different.
   489  // Besides, at most one TokenRequest can ignore Audience.
   490  func validateTokenRequests(tokenRequests []storage.TokenRequest, fldPath *field.Path) field.ErrorList {
   491  	const min = 10 * time.Minute
   492  	allErrs := field.ErrorList{}
   493  	audiences := make(map[string]bool)
   494  	for i, tokenRequest := range tokenRequests {
   495  		path := fldPath.Index(i)
   496  		audience := tokenRequest.Audience
   497  		if _, ok := audiences[audience]; ok {
   498  			allErrs = append(allErrs, field.Duplicate(path.Child("audience"), audience))
   499  			continue
   500  		}
   501  		audiences[audience] = true
   502  
   503  		if tokenRequest.ExpirationSeconds == nil {
   504  			continue
   505  		}
   506  		if *tokenRequest.ExpirationSeconds < int64(min.Seconds()) {
   507  			allErrs = append(allErrs, field.Invalid(path.Child("expirationSeconds"), *tokenRequest.ExpirationSeconds, "may not specify a duration less than 10 minutes"))
   508  		}
   509  		if *tokenRequest.ExpirationSeconds > 1<<32 {
   510  			allErrs = append(allErrs, field.Invalid(path.Child("expirationSeconds"), *tokenRequest.ExpirationSeconds, "may not specify a duration larger than 2^32 seconds"))
   511  		}
   512  	}
   513  
   514  	return allErrs
   515  }
   516  
   517  // validateVolumeLifecycleModes tests if mode has one of the allowed values.
   518  func validateVolumeLifecycleModes(modes []storage.VolumeLifecycleMode, fldPath *field.Path) field.ErrorList {
   519  	allErrs := field.ErrorList{}
   520  	for _, mode := range modes {
   521  		switch mode {
   522  		case storage.VolumeLifecyclePersistent, storage.VolumeLifecycleEphemeral:
   523  		default:
   524  			allErrs = append(allErrs, field.NotSupported(fldPath, mode,
   525  				[]string{
   526  					string(storage.VolumeLifecyclePersistent),
   527  					string(storage.VolumeLifecycleEphemeral),
   528  				}))
   529  		}
   530  	}
   531  
   532  	return allErrs
   533  }
   534  
   535  // validateSELinuxMount tests if seLinuxMount is set for CSIDriver.
   536  func validateSELinuxMount(seLinuxMount *bool, fldPath *field.Path) field.ErrorList {
   537  	allErrs := field.ErrorList{}
   538  	if seLinuxMount == nil && utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) {
   539  		allErrs = append(allErrs, field.Required(fldPath, ""))
   540  	}
   541  
   542  	return allErrs
   543  }
   544  
   545  // ValidateStorageCapacityName checks that a name is appropriate for a
   546  // CSIStorageCapacity object.
   547  var ValidateStorageCapacityName = apimachineryvalidation.NameIsDNSSubdomain
   548  
   549  type CSIStorageCapacityValidateOptions struct {
   550  	AllowInvalidLabelValueInSelector bool
   551  }
   552  
   553  // ValidateCSIStorageCapacity validates a CSIStorageCapacity.
   554  func ValidateCSIStorageCapacity(capacity *storage.CSIStorageCapacity, opts CSIStorageCapacityValidateOptions) field.ErrorList {
   555  	allErrs := apivalidation.ValidateObjectMeta(&capacity.ObjectMeta, true, ValidateStorageCapacityName, field.NewPath("metadata"))
   556  	labelSelectorValidationOptions := metav1validation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector}
   557  	allErrs = append(allErrs, metav1validation.ValidateLabelSelector(capacity.NodeTopology, labelSelectorValidationOptions, field.NewPath("nodeTopology"))...)
   558  	for _, msg := range apivalidation.ValidateClassName(capacity.StorageClassName, false) {
   559  		allErrs = append(allErrs, field.Invalid(field.NewPath("storageClassName"), capacity.StorageClassName, msg))
   560  	}
   561  	if capacity.Capacity != nil {
   562  		allErrs = append(allErrs, apivalidation.ValidateNonnegativeQuantity(*capacity.Capacity, field.NewPath("capacity"))...)
   563  	}
   564  	return allErrs
   565  }
   566  
   567  // ValidateCSIStorageCapacityUpdate tests if an update to CSIStorageCapacity is valid.
   568  func ValidateCSIStorageCapacityUpdate(capacity, oldCapacity *storage.CSIStorageCapacity) field.ErrorList {
   569  	allErrs := apivalidation.ValidateObjectMetaUpdate(&capacity.ObjectMeta, &oldCapacity.ObjectMeta, field.NewPath("metadata"))
   570  
   571  	// Input fields for CSI GetCapacity are immutable.
   572  	// If this ever relaxes in the future, make sure to increment the Generation number in PrepareForUpdate
   573  	if !apiequality.Semantic.DeepEqual(capacity.NodeTopology, oldCapacity.NodeTopology) {
   574  		allErrs = append(allErrs, field.Invalid(field.NewPath("nodeTopology"), capacity.NodeTopology, "field is immutable"))
   575  	}
   576  	if capacity.StorageClassName != oldCapacity.StorageClassName {
   577  		allErrs = append(allErrs, field.Invalid(field.NewPath("storageClassName"), capacity.StorageClassName, "field is immutable"))
   578  	}
   579  
   580  	return allErrs
   581  }
   582  
   583  // ValidateVolumeAttributesClass validates a VolumeAttributesClass.
   584  func ValidateVolumeAttributesClass(volumeAttributesClass *storage.VolumeAttributesClass) field.ErrorList {
   585  	allErrs := apivalidation.ValidateObjectMeta(&volumeAttributesClass.ObjectMeta, false, apivalidation.ValidateClassName, field.NewPath("metadata"))
   586  	allErrs = append(allErrs, validateProvisioner(volumeAttributesClass.DriverName, field.NewPath("driverName"))...)
   587  	allErrs = append(allErrs, validateParameters(volumeAttributesClass.Parameters, false, field.NewPath("parameters"))...)
   588  	return allErrs
   589  }
   590  
   591  // ValidateVolumeAttributesClassUpdate tests if an update to VolumeAttributesClass is valid.
   592  func ValidateVolumeAttributesClassUpdate(volumeAttributesClass, oldVolumeAttributesClass *storage.VolumeAttributesClass) field.ErrorList {
   593  	allErrs := apivalidation.ValidateObjectMetaUpdate(&volumeAttributesClass.ObjectMeta, &oldVolumeAttributesClass.ObjectMeta, field.NewPath("metadata"))
   594  	if volumeAttributesClass.DriverName != oldVolumeAttributesClass.DriverName {
   595  		allErrs = append(allErrs, field.Forbidden(field.NewPath("driverName"), "updates to driverName are forbidden."))
   596  	}
   597  	if !reflect.DeepEqual(oldVolumeAttributesClass.Parameters, volumeAttributesClass.Parameters) {
   598  		allErrs = append(allErrs, field.Forbidden(field.NewPath("parameters"), "updates to parameters are forbidden."))
   599  	}
   600  	allErrs = append(allErrs, ValidateVolumeAttributesClass(volumeAttributesClass)...)
   601  	return allErrs
   602  }
   603  

View as plain text