@@ -372,9 +372,16 @@ func (gceCS *GCEControllerServer) createVolumeInternal(ctx context.Context, req
372
372
return nil , err
373
373
}
374
374
375
+ // Validate and determine multi-zone provisioning configuration
376
+ multiZoneVolume , err := gceCS .getRequestIsMultiZone (req , params )
377
+ if err != nil {
378
+ return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to validate multi-zone provisioning request: %v" , err )
379
+ }
380
+
375
381
// Validate VolumeContentSource is set when access mode is read only
376
382
readonly , _ := getReadOnlyFromCapabilities (volumeCapabilities )
377
- if readonly && req .GetVolumeContentSource () == nil {
383
+ if readonly && req .GetVolumeContentSource () == nil && ! multiZoneVolume {
384
+ // Allow readonly volumes to be dynamically created for multi-zone volumes.
378
385
return nil , status .Error (codes .InvalidArgument , "VolumeContentSource must be provided when AccessMode is set to read only" )
379
386
}
380
387
@@ -391,12 +398,6 @@ func (gceCS *GCEControllerServer) createVolumeInternal(ctx context.Context, req
391
398
return nil , status .Errorf (codes .InvalidArgument , "Invalid access mode for disk type %s" , params .DiskType )
392
399
}
393
400
394
- // Validate multi-zone provisioning configuration
395
- err = gceCS .validateMultiZoneProvisioning (req , params )
396
- if err != nil {
397
- return nil , status .Errorf (codes .InvalidArgument , "CreateVolume failed to validate multi-zone provisioning request: %v" , err )
398
- }
399
-
400
401
// Verify that the regional availability class is only used on regional disks.
401
402
if params .ForceAttach && ! params .IsRegional () {
402
403
return nil , status .Errorf (codes .InvalidArgument , "invalid availabilty class for zonal disk" )
@@ -485,12 +486,19 @@ func (gceCS *GCEControllerServer) createMultiZoneDisk(ctx context.Context, req *
485
486
}
486
487
defer gceCS .volumeLocks .Release (volumeID )
487
488
489
+ // If creating an empty disk (content source nil), always create RWO disks
490
+ // This allows multi-zone disks to be created as underlying RWO disks, so they can be hydrated.
491
+ accessMode := common .GCEReadWriteOnceAccessMode
492
+ if req .GetVolumeContentSource () != nil {
493
+ accessMode = common .GCEReadOnlyManyAccessMode
494
+ }
495
+
488
496
createDiskErrs := []error {}
489
497
createdDisks := make ([]* gce.CloudDisk , 0 , len (zones ))
490
498
for _ , zone := range zones {
491
499
volKey := meta .ZonalKey (req .GetName (), zone )
492
500
klog .V (4 ).Infof ("Creating single zone disk for zone %q and volume: %v" , zone , volKey )
493
- disk , err := gceCS .createSingleDisk (ctx , req , params , volKey , []string {zone })
501
+ disk , err := gceCS .createSingleDisk (ctx , req , params , volKey , []string {zone }, accessMode )
494
502
if err != nil {
495
503
createDiskErrs = append (createDiskErrs , err )
496
504
continue
@@ -593,11 +601,16 @@ func (gceCS *GCEControllerServer) createSingleDeviceDisk(ctx context.Context, re
593
601
if err != nil {
594
602
return nil , common .LoggedError ("Failed to convert volume key to volume ID: " , err )
595
603
}
604
+ accessMode , err := getAccessMode (req , params )
605
+ if err != nil {
606
+ return nil , common .LoggedError ("Failed to get access mode: " , err )
607
+ }
608
+
596
609
if acquired := gceCS .volumeLocks .TryAcquire (volumeID ); ! acquired {
597
610
return nil , status .Errorf (codes .Aborted , common .VolumeOperationAlreadyExistsFmt , volumeID )
598
611
}
599
612
defer gceCS .volumeLocks .Release (volumeID )
600
- disk , err := gceCS .createSingleDisk (ctx , req , params , volKey , zones )
613
+ disk , err := gceCS .createSingleDisk (ctx , req , params , volKey , zones , accessMode )
601
614
602
615
if err != nil {
603
616
return nil , common .LoggedError ("CreateVolume failed: %v" , err )
@@ -606,30 +619,36 @@ func (gceCS *GCEControllerServer) createSingleDeviceDisk(ctx context.Context, re
606
619
return generateCreateVolumeResponseWithVolumeId (disk , zones , params , dataCacheParams , enableDataCache , volumeID ), err
607
620
}
608
621
609
- func (gceCS * GCEControllerServer ) createSingleDisk (ctx context.Context , req * csi.CreateVolumeRequest , params common.DiskParameters , volKey * meta.Key , zones []string ) (* gce.CloudDisk , error ) {
610
- capacityRange := req .GetCapacityRange ()
611
- capBytes , _ := getRequestCapacity (capacityRange )
622
+ func getAccessMode (req * csi.CreateVolumeRequest , params common.DiskParameters ) (string , error ) {
612
623
readonly , _ := getReadOnlyFromCapabilities (req .GetVolumeCapabilities ())
613
- accessMode := ""
614
- multiWriter := false
615
624
if common .IsHyperdisk (params .DiskType ) {
616
625
if am , err := getHyperdiskAccessModeFromCapabilities (req .GetVolumeCapabilities ()); err != nil {
617
- return nil , err
626
+ return "" , err
618
627
} else if disksWithUnsettableAccessMode [params .DiskType ] {
619
628
// Disallow multi-attach for HdT and HdE. These checks were done in `createVolumeInternal`,
620
629
// but repeating them here future-proves us from possible refactors.
621
630
if am != common .GCEReadWriteOnceAccessMode {
622
- return nil , status .Errorf (codes .Internal , "" )
631
+ return "" , status .Errorf (codes .Internal , "" )
623
632
}
624
633
} else {
625
- accessMode = am
634
+ return am , nil
626
635
}
627
- } else {
628
- multiWriter , _ = getMultiWriterFromCapabilities (req .GetVolumeCapabilities ())
629
636
}
630
637
631
638
if readonly && slices .Contains (disksWithModifiableAccessMode , params .DiskType ) {
632
- accessMode = common .GCEReadOnlyManyAccessMode
639
+ return common .GCEReadOnlyManyAccessMode , nil
640
+ }
641
+
642
+ return "" , nil
643
+ }
644
+
645
+ func (gceCS * GCEControllerServer ) createSingleDisk (ctx context.Context , req * csi.CreateVolumeRequest , params common.DiskParameters , volKey * meta.Key , zones []string , accessMode string ) (* gce.CloudDisk , error ) {
646
+ capacityRange := req .GetCapacityRange ()
647
+ capBytes , _ := getRequestCapacity (capacityRange )
648
+
649
+ multiWriter := false
650
+ if ! common .IsHyperdisk (params .DiskType ) {
651
+ multiWriter , _ = getMultiWriterFromCapabilities (req .GetVolumeCapabilities ())
633
652
}
634
653
635
654
// Validate if disk already exists
@@ -1039,31 +1058,31 @@ func (gceCS *GCEControllerServer) validateMultiZoneDisk(volumeID string, disk *g
1039
1058
return nil
1040
1059
}
1041
1060
1042
- func (gceCS * GCEControllerServer ) validateMultiZoneProvisioning (req * csi.CreateVolumeRequest , params common.DiskParameters ) error {
1061
+ func (gceCS * GCEControllerServer ) getRequestIsMultiZone (req * csi.CreateVolumeRequest , params common.DiskParameters ) ( bool , error ) {
1043
1062
if ! gceCS .multiZoneVolumeHandleConfig .Enable {
1044
- return nil
1063
+ return false , nil
1045
1064
}
1046
1065
if ! params .MultiZoneProvisioning {
1047
- return nil
1066
+ return false , nil
1048
1067
}
1049
1068
1050
1069
// For volume populator, we want to allow multiple RWO disks to be created
1051
1070
// with the same name, so they can be hydrated across multiple zones.
1052
1071
1053
1072
// We don't have support volume cloning from an existing PVC
1054
1073
if useVolumeCloning (req ) {
1055
- return fmt .Errorf ("%q parameter does not support volume cloning" , common .ParameterKeyEnableMultiZoneProvisioning )
1074
+ return false , fmt .Errorf ("%q parameter does not support volume cloning" , common .ParameterKeyEnableMultiZoneProvisioning )
1056
1075
}
1057
1076
1058
1077
if readonly , _ := getReadOnlyFromCapabilities (req .GetVolumeCapabilities ()); ! readonly && req .GetVolumeContentSource () != nil {
1059
- return fmt .Errorf ("%q parameter does not support specifying volume content source in readwrite mode" , common .ParameterKeyEnableMultiZoneProvisioning )
1078
+ return false , fmt .Errorf ("%q parameter does not support specifying volume content source in readwrite mode" , common .ParameterKeyEnableMultiZoneProvisioning )
1060
1079
}
1061
1080
1062
1081
if ! slices .Contains (gceCS .multiZoneVolumeHandleConfig .DiskTypes , params .DiskType ) {
1063
- return fmt .Errorf ("%q parameter with unsupported disk type: %v" , common .ParameterKeyEnableMultiZoneProvisioning , params .DiskType )
1082
+ return false , fmt .Errorf ("%q parameter with unsupported disk type: %v" , common .ParameterKeyEnableMultiZoneProvisioning , params .DiskType )
1064
1083
}
1065
1084
1066
- return nil
1085
+ return true , nil
1067
1086
}
1068
1087
1069
1088
func isMultiZoneVolKey (volumeKey * meta.Key ) bool {
0 commit comments