1
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)
40 maxProvisionerParameterLen = 512
41
42 maxAttachedVolumeMetadataSize = 256 * (1 << 10)
43 maxVolumeErrorMessageSize = 1024
44
45 csiNodeIDMaxLength = 192
46 csiNodeIDLongerMaxLength = 256
47 )
48
49
50 type CSINodeValidationOptions struct {
51 AllowLongNodeID bool
52 }
53
54
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
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
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
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
127
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
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
147
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
161
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
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
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
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
201 func validateNodeName(nodeName string, fldPath *field.Path) field.ErrorList {
202 allErrs := field.ErrorList{}
203 for _, msg := range apivalidation.ValidateNodeName(nodeName, false ) {
204 allErrs = append(allErrs, field.Invalid(fldPath, nodeName, msg))
205 }
206 return allErrs
207 }
208
209
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
244 func ValidateVolumeAttachmentUpdate(new, old *storage.VolumeAttachment) field.ErrorList {
245 allErrs := ValidateVolumeAttachment(new)
246
247
248
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
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
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
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
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
304 func ValidateCSINodeUpdate(new, old *storage.CSINode, validationOpts CSINodeValidationOptions) field.ErrorList {
305 allErrs := ValidateCSINode(new, validationOpts)
306
307
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
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
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
342 func validateCSINodeDriverNodeID(nodeID string, fldPath *field.Path, validationOpts CSINodeValidationOptions) field.ErrorList {
343 allErrs := field.ErrorList{}
344
345
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
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
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
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
403
404 var ValidateCSIDriverName = apimachineryvalidation.NameIsDNSSubdomain
405
406
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
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
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
427
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
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
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
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
474 func validateFSGroupPolicy(fsGroupPolicy *storage.FSGroupPolicy, fldPath *field.Path) field.ErrorList {
475 allErrs := field.ErrorList{}
476 if fsGroupPolicy == nil {
477
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
489
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
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
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
546
547 var ValidateStorageCapacityName = apimachineryvalidation.NameIsDNSSubdomain
548
549 type CSIStorageCapacityValidateOptions struct {
550 AllowInvalidLabelValueInSelector bool
551 }
552
553
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
568 func ValidateCSIStorageCapacityUpdate(capacity, oldCapacity *storage.CSIStorageCapacity) field.ErrorList {
569 allErrs := apivalidation.ValidateObjectMetaUpdate(&capacity.ObjectMeta, &oldCapacity.ObjectMeta, field.NewPath("metadata"))
570
571
572
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
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
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