@@ -319,7 +319,8 @@ func validateGuestPVCOperation(ctx context.Context, req *admissionv1.AdmissionRe
319319
320320 // ValidateLinkedCloneRequest validates the various conditions necessary for a valid linkedclone request.
321321 // 1. The PVC datasource needs to be of type VolumeSnapshot
322- // 2. The storageclass associated LinkedClone PVC should be the same the source PVC
322+ // 2. The svStorageClass parameter in the storageclass associated with the LinkedClone PVC
323+ // should match the svStorageClass in the storageclass of the source PVC
323324 // 3. The size should be the same as the source PVC
324325 // 4. Should not be a second level LinkedClone
325326 // 5. VS is not under deletion
@@ -462,14 +463,129 @@ func validateGuestPVCOperation(ctx context.Context, req *admissionv1.AdmissionRe
462463 },
463464 }
464465 }
465-
466- // The storageclass associated LinkedClone PVC should be the same the source PVC
466+ // The svStorageClass parameter in the storageclass associated with the LinkedClone PVC
467+ // should be the same as the svStorageClass in the storageclass of the source PVC
467468 sourcePVCStorageClassName := sourcePVC .Spec .StorageClassName
468- same := strings .Compare (* pvc .Spec .StorageClassName , * sourcePVCStorageClassName )
469- if same != 0 {
470- errMsg := fmt .Sprintf ("StorageClass mismatch, Namespace: %s, LinkedClone StorageClass: " +
471- "%s, source PVC StorageClass: %s" , sourcePVC .Namespace , * pvc .Spec .StorageClassName ,
472- * sourcePVCStorageClassName )
469+ linkedClonePVCStorageClassName := pvc .Spec .StorageClassName
470+
471+ // Validate source PVC StorageClass
472+ if sourcePVCStorageClassName == nil {
473+ errMsg := "source PVC does not have a StorageClass specified, please specify a StorageClass for linked clone creation"
474+ return & admissionv1.AdmissionResponse {
475+ Allowed : false ,
476+ Result : & metav1.Status {
477+ Message : errMsg ,
478+ },
479+ }
480+ }
481+ if * sourcePVCStorageClassName == "" {
482+ errMsg := "source PVC has an empty StorageClass name and cannot be validated for linked clone creation"
483+ return & admissionv1.AdmissionResponse {
484+ Allowed : false ,
485+ Result : & metav1.Status {
486+ Message : errMsg ,
487+ },
488+ }
489+ }
490+
491+ // Validate LinkedClone PVC StorageClass
492+ if linkedClonePVCStorageClassName == nil {
493+ errMsg := "LinkedClone PVC does not have a StorageClass specified, please specify a StorageClass for linked clone creation"
494+ return & admissionv1.AdmissionResponse {
495+ Allowed : false ,
496+ Result : & metav1.Status {
497+ Message : errMsg ,
498+ },
499+ }
500+ }
501+ if * linkedClonePVCStorageClassName == "" {
502+ errMsg := "LinkedClone PVC has an empty StorageClass name and cannot be validated for linked clone creation"
503+ return & admissionv1.AdmissionResponse {
504+ Allowed : false ,
505+ Result : & metav1.Status {
506+ Message : errMsg ,
507+ },
508+ }
509+ }
510+
511+ // Retrieve the StorageClass objects
512+ sourceStorageClass , err := k8sClient .StorageV1 ().StorageClasses ().Get (ctx , * sourcePVCStorageClassName ,
513+ metav1.GetOptions {})
514+ if err != nil {
515+ errMsg := fmt .Sprintf ("error getting source PVC StorageClass %s from api server: %v" ,
516+ * sourcePVCStorageClassName , err )
517+ return & admissionv1.AdmissionResponse {
518+ Allowed : false ,
519+ Result : & metav1.Status {
520+ Message : errMsg ,
521+ },
522+ }
523+ }
524+
525+ linkedCloneStorageClass , err := k8sClient .StorageV1 ().StorageClasses ().Get (ctx , * linkedClonePVCStorageClassName ,
526+ metav1.GetOptions {})
527+ if err != nil {
528+ errMsg := fmt .Sprintf ("error getting LinkedClone PVC StorageClass %s from api server: %v" ,
529+ * linkedClonePVCStorageClassName , err )
530+ return & admissionv1.AdmissionResponse {
531+ Allowed : false ,
532+ Result : & metav1.Status {
533+ Message : errMsg ,
534+ },
535+ }
536+ }
537+
538+ // Extract svStorageClass from StorageClass parameters (case-insensitive lookup)
539+ var sourceSvStorageClass string
540+ var sourceHasSvStorageClass bool
541+ for param , value := range sourceStorageClass .Parameters {
542+ if strings .ToLower (param ) == common .AttributeSupervisorStorageClass {
543+ sourceSvStorageClass = value
544+ sourceHasSvStorageClass = true
545+ break
546+ }
547+ }
548+
549+ var linkedCloneSvStorageClass string
550+ var linkedCloneHasSvStorageClass bool
551+ for param , value := range linkedCloneStorageClass .Parameters {
552+ if strings .ToLower (param ) == common .AttributeSupervisorStorageClass {
553+ linkedCloneSvStorageClass = value
554+ linkedCloneHasSvStorageClass = true
555+ break
556+ }
557+ }
558+
559+ // Both storage classes must have svStorageClass parameter
560+ if ! sourceHasSvStorageClass {
561+ errMsg := fmt .Sprintf ("source PVC StorageClass %s does not have %s parameter" ,
562+ * sourcePVCStorageClassName , common .AttributeSupervisorStorageClass )
563+ return & admissionv1.AdmissionResponse {
564+ Allowed : false ,
565+ Result : & metav1.Status {
566+ Message : errMsg ,
567+ },
568+ }
569+ }
570+
571+ if ! linkedCloneHasSvStorageClass {
572+ errMsg := fmt .Sprintf ("LinkedClone PVC StorageClass %s does not have %s parameter" ,
573+ * linkedClonePVCStorageClassName , common .AttributeSupervisorStorageClass )
574+ return & admissionv1.AdmissionResponse {
575+ Allowed : false ,
576+ Result : & metav1.Status {
577+ Message : errMsg ,
578+ },
579+ }
580+ }
581+
582+ // Compare svStorageClass values
583+ if sourceSvStorageClass != linkedCloneSvStorageClass {
584+ errMsg := fmt .Sprintf ("StorageClass svStorageClass mismatch, Namespace: %s, " +
585+ "LinkedClone StorageClass: %s (svStorageClass: %s), " +
586+ "source PVC StorageClass: %s (svStorageClass: %s)" ,
587+ sourcePVC .Namespace , * linkedClonePVCStorageClassName , linkedCloneSvStorageClass ,
588+ * sourcePVCStorageClassName , sourceSvStorageClass )
473589 return & admissionv1.AdmissionResponse {
474590 Allowed : false ,
475591 Result : & metav1.Status {
0 commit comments