k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/apis/storage/validation/validation.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package 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 }