k8s.io/kubernetes@v1.29.3/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.FSGroupPolicy, old.Spec.FSGroupPolicy, field.NewPath("spec", "fsGroupPolicy"))...) 422 allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(new.Spec.PodInfoOnMount, old.Spec.PodInfoOnMount, field.NewPath("spec", "podInfoOnMount"))...) 423 allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(new.Spec.VolumeLifecycleModes, old.Spec.VolumeLifecycleModes, field.NewPath("spec", "volumeLifecycleModes"))...) 424 425 allErrs = append(allErrs, validateTokenRequests(new.Spec.TokenRequests, field.NewPath("spec", "tokenRequests"))...) 426 return allErrs 427 } 428 429 // ValidateCSIDriverSpec tests that the specified CSIDriverSpec 430 // has valid data. 431 func validateCSIDriverSpec( 432 spec *storage.CSIDriverSpec, fldPath *field.Path) field.ErrorList { 433 allErrs := field.ErrorList{} 434 allErrs = append(allErrs, validateAttachRequired(spec.AttachRequired, fldPath.Child("attachedRequired"))...) 435 allErrs = append(allErrs, validatePodInfoOnMount(spec.PodInfoOnMount, fldPath.Child("podInfoOnMount"))...) 436 allErrs = append(allErrs, validateStorageCapacity(spec.StorageCapacity, fldPath.Child("storageCapacity"))...) 437 allErrs = append(allErrs, validateFSGroupPolicy(spec.FSGroupPolicy, fldPath.Child("fsGroupPolicy"))...) 438 allErrs = append(allErrs, validateTokenRequests(spec.TokenRequests, fldPath.Child("tokenRequests"))...) 439 allErrs = append(allErrs, validateVolumeLifecycleModes(spec.VolumeLifecycleModes, fldPath.Child("volumeLifecycleModes"))...) 440 allErrs = append(allErrs, validateSELinuxMount(spec.SELinuxMount, fldPath.Child("seLinuxMount"))...) 441 return allErrs 442 } 443 444 // validateAttachRequired tests if attachRequired is set for CSIDriver. 445 func validateAttachRequired(attachRequired *bool, fldPath *field.Path) field.ErrorList { 446 allErrs := field.ErrorList{} 447 if attachRequired == nil { 448 allErrs = append(allErrs, field.Required(fldPath, "")) 449 } 450 451 return allErrs 452 } 453 454 // validatePodInfoOnMount tests if podInfoOnMount is set for CSIDriver. 455 func validatePodInfoOnMount(podInfoOnMount *bool, fldPath *field.Path) field.ErrorList { 456 allErrs := field.ErrorList{} 457 if podInfoOnMount == nil { 458 allErrs = append(allErrs, field.Required(fldPath, "")) 459 } 460 461 return allErrs 462 } 463 464 // validateStorageCapacity tests if storageCapacity is set for CSIDriver. 465 func validateStorageCapacity(storageCapacity *bool, fldPath *field.Path) field.ErrorList { 466 allErrs := field.ErrorList{} 467 if storageCapacity == nil { 468 allErrs = append(allErrs, field.Required(fldPath, "")) 469 } 470 471 return allErrs 472 } 473 474 var supportedFSGroupPolicy = sets.NewString(string(storage.ReadWriteOnceWithFSTypeFSGroupPolicy), string(storage.FileFSGroupPolicy), string(storage.NoneFSGroupPolicy)) 475 476 // validateFSGroupPolicy tests if FSGroupPolicy contains an appropriate value. 477 func validateFSGroupPolicy(fsGroupPolicy *storage.FSGroupPolicy, fldPath *field.Path) field.ErrorList { 478 allErrs := field.ErrorList{} 479 if fsGroupPolicy == nil { 480 // This is not a required field, so if nothing is provided simply return 481 return allErrs 482 } 483 484 if !supportedFSGroupPolicy.Has(string(*fsGroupPolicy)) { 485 allErrs = append(allErrs, field.NotSupported(fldPath, fsGroupPolicy, supportedFSGroupPolicy.List())) 486 } 487 488 return allErrs 489 } 490 491 // validateTokenRequests tests if the Audience in each TokenRequest are different. 492 // Besides, at most one TokenRequest can ignore Audience. 493 func validateTokenRequests(tokenRequests []storage.TokenRequest, fldPath *field.Path) field.ErrorList { 494 const min = 10 * time.Minute 495 allErrs := field.ErrorList{} 496 audiences := make(map[string]bool) 497 for i, tokenRequest := range tokenRequests { 498 path := fldPath.Index(i) 499 audience := tokenRequest.Audience 500 if _, ok := audiences[audience]; ok { 501 allErrs = append(allErrs, field.Duplicate(path.Child("audience"), audience)) 502 continue 503 } 504 audiences[audience] = true 505 506 if tokenRequest.ExpirationSeconds == nil { 507 continue 508 } 509 if *tokenRequest.ExpirationSeconds < int64(min.Seconds()) { 510 allErrs = append(allErrs, field.Invalid(path.Child("expirationSeconds"), *tokenRequest.ExpirationSeconds, "may not specify a duration less than 10 minutes")) 511 } 512 if *tokenRequest.ExpirationSeconds > 1<<32 { 513 allErrs = append(allErrs, field.Invalid(path.Child("expirationSeconds"), *tokenRequest.ExpirationSeconds, "may not specify a duration larger than 2^32 seconds")) 514 } 515 } 516 517 return allErrs 518 } 519 520 // validateVolumeLifecycleModes tests if mode has one of the allowed values. 521 func validateVolumeLifecycleModes(modes []storage.VolumeLifecycleMode, fldPath *field.Path) field.ErrorList { 522 allErrs := field.ErrorList{} 523 for _, mode := range modes { 524 switch mode { 525 case storage.VolumeLifecyclePersistent, storage.VolumeLifecycleEphemeral: 526 default: 527 allErrs = append(allErrs, field.NotSupported(fldPath, mode, 528 []string{ 529 string(storage.VolumeLifecyclePersistent), 530 string(storage.VolumeLifecycleEphemeral), 531 })) 532 } 533 } 534 535 return allErrs 536 } 537 538 // validateSELinuxMount tests if seLinuxMount is set for CSIDriver. 539 func validateSELinuxMount(seLinuxMount *bool, fldPath *field.Path) field.ErrorList { 540 allErrs := field.ErrorList{} 541 if seLinuxMount == nil && utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 542 allErrs = append(allErrs, field.Required(fldPath, "")) 543 } 544 545 return allErrs 546 } 547 548 // ValidateStorageCapacityName checks that a name is appropriate for a 549 // CSIStorageCapacity object. 550 var ValidateStorageCapacityName = apimachineryvalidation.NameIsDNSSubdomain 551 552 type CSIStorageCapacityValidateOptions struct { 553 AllowInvalidLabelValueInSelector bool 554 } 555 556 // ValidateCSIStorageCapacity validates a CSIStorageCapacity. 557 func ValidateCSIStorageCapacity(capacity *storage.CSIStorageCapacity, opts CSIStorageCapacityValidateOptions) field.ErrorList { 558 allErrs := apivalidation.ValidateObjectMeta(&capacity.ObjectMeta, true, ValidateStorageCapacityName, field.NewPath("metadata")) 559 labelSelectorValidationOptions := metav1validation.LabelSelectorValidationOptions{AllowInvalidLabelValueInSelector: opts.AllowInvalidLabelValueInSelector} 560 allErrs = append(allErrs, metav1validation.ValidateLabelSelector(capacity.NodeTopology, labelSelectorValidationOptions, field.NewPath("nodeTopology"))...) 561 for _, msg := range apivalidation.ValidateClassName(capacity.StorageClassName, false) { 562 allErrs = append(allErrs, field.Invalid(field.NewPath("storageClassName"), capacity.StorageClassName, msg)) 563 } 564 if capacity.Capacity != nil { 565 allErrs = append(allErrs, apivalidation.ValidateNonnegativeQuantity(*capacity.Capacity, field.NewPath("capacity"))...) 566 } 567 return allErrs 568 } 569 570 // ValidateCSIStorageCapacityUpdate tests if an update to CSIStorageCapacity is valid. 571 func ValidateCSIStorageCapacityUpdate(capacity, oldCapacity *storage.CSIStorageCapacity) field.ErrorList { 572 allErrs := apivalidation.ValidateObjectMetaUpdate(&capacity.ObjectMeta, &oldCapacity.ObjectMeta, field.NewPath("metadata")) 573 574 // Input fields for CSI GetCapacity are immutable. 575 // If this ever relaxes in the future, make sure to increment the Generation number in PrepareForUpdate 576 if !apiequality.Semantic.DeepEqual(capacity.NodeTopology, oldCapacity.NodeTopology) { 577 allErrs = append(allErrs, field.Invalid(field.NewPath("nodeTopology"), capacity.NodeTopology, "field is immutable")) 578 } 579 if capacity.StorageClassName != oldCapacity.StorageClassName { 580 allErrs = append(allErrs, field.Invalid(field.NewPath("storageClassName"), capacity.StorageClassName, "field is immutable")) 581 } 582 583 return allErrs 584 } 585 586 // ValidateVolumeAttributesClass validates a VolumeAttributesClass. 587 func ValidateVolumeAttributesClass(volumeAttributesClass *storage.VolumeAttributesClass) field.ErrorList { 588 allErrs := apivalidation.ValidateObjectMeta(&volumeAttributesClass.ObjectMeta, false, apivalidation.ValidateClassName, field.NewPath("metadata")) 589 allErrs = append(allErrs, validateProvisioner(volumeAttributesClass.DriverName, field.NewPath("driverName"))...) 590 allErrs = append(allErrs, validateParameters(volumeAttributesClass.Parameters, false, field.NewPath("parameters"))...) 591 return allErrs 592 } 593 594 // ValidateVolumeAttributesClassUpdate tests if an update to VolumeAttributesClass is valid. 595 func ValidateVolumeAttributesClassUpdate(volumeAttributesClass, oldVolumeAttributesClass *storage.VolumeAttributesClass) field.ErrorList { 596 allErrs := apivalidation.ValidateObjectMetaUpdate(&volumeAttributesClass.ObjectMeta, &oldVolumeAttributesClass.ObjectMeta, field.NewPath("metadata")) 597 if volumeAttributesClass.DriverName != oldVolumeAttributesClass.DriverName { 598 allErrs = append(allErrs, field.Forbidden(field.NewPath("driverName"), "updates to driverName are forbidden.")) 599 } 600 if !reflect.DeepEqual(oldVolumeAttributesClass.Parameters, volumeAttributesClass.Parameters) { 601 allErrs = append(allErrs, field.Forbidden(field.NewPath("parameters"), "updates to parameters are forbidden.")) 602 } 603 return allErrs 604 }