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