Skip to content

Commit 21f8bc4

Browse files
committed
Relax volumeContentSource restriction for ROX multi-zone dynamic volume creation. This enables empty ROX multi-zone PVCs to be created, to allow for static hydration of disks
1 parent 0be5a10 commit 21f8bc4

File tree

3 files changed

+199
-99
lines changed

3 files changed

+199
-99
lines changed

pkg/gce-pd-csi-driver/controller.go

+46-27
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,16 @@ func (gceCS *GCEControllerServer) createVolumeInternal(ctx context.Context, req
372372
return nil, err
373373
}
374374

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+
375381
// Validate VolumeContentSource is set when access mode is read only
376382
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.
378385
return nil, status.Error(codes.InvalidArgument, "VolumeContentSource must be provided when AccessMode is set to read only")
379386
}
380387

@@ -391,12 +398,6 @@ func (gceCS *GCEControllerServer) createVolumeInternal(ctx context.Context, req
391398
return nil, status.Errorf(codes.InvalidArgument, "Invalid access mode for disk type %s", params.DiskType)
392399
}
393400

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-
400401
// Verify that the regional availability class is only used on regional disks.
401402
if params.ForceAttach && !params.IsRegional() {
402403
return nil, status.Errorf(codes.InvalidArgument, "invalid availabilty class for zonal disk")
@@ -485,12 +486,19 @@ func (gceCS *GCEControllerServer) createMultiZoneDisk(ctx context.Context, req *
485486
}
486487
defer gceCS.volumeLocks.Release(volumeID)
487488

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+
488496
createDiskErrs := []error{}
489497
createdDisks := make([]*gce.CloudDisk, 0, len(zones))
490498
for _, zone := range zones {
491499
volKey := meta.ZonalKey(req.GetName(), zone)
492500
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)
494502
if err != nil {
495503
createDiskErrs = append(createDiskErrs, err)
496504
continue
@@ -593,11 +601,16 @@ func (gceCS *GCEControllerServer) createSingleDeviceDisk(ctx context.Context, re
593601
if err != nil {
594602
return nil, common.LoggedError("Failed to convert volume key to volume ID: ", err)
595603
}
604+
accessMode, err := getAccessMode(req, params)
605+
if err != nil {
606+
return nil, common.LoggedError("Failed to get access mode: ", err)
607+
}
608+
596609
if acquired := gceCS.volumeLocks.TryAcquire(volumeID); !acquired {
597610
return nil, status.Errorf(codes.Aborted, common.VolumeOperationAlreadyExistsFmt, volumeID)
598611
}
599612
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)
601614

602615
if err != nil {
603616
return nil, common.LoggedError("CreateVolume failed: %v", err)
@@ -606,30 +619,36 @@ func (gceCS *GCEControllerServer) createSingleDeviceDisk(ctx context.Context, re
606619
return generateCreateVolumeResponseWithVolumeId(disk, zones, params, dataCacheParams, enableDataCache, volumeID), err
607620
}
608621

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) {
612623
readonly, _ := getReadOnlyFromCapabilities(req.GetVolumeCapabilities())
613-
accessMode := ""
614-
multiWriter := false
615624
if common.IsHyperdisk(params.DiskType) {
616625
if am, err := getHyperdiskAccessModeFromCapabilities(req.GetVolumeCapabilities()); err != nil {
617-
return nil, err
626+
return "", err
618627
} else if disksWithUnsettableAccessMode[params.DiskType] {
619628
// Disallow multi-attach for HdT and HdE. These checks were done in `createVolumeInternal`,
620629
// but repeating them here future-proves us from possible refactors.
621630
if am != common.GCEReadWriteOnceAccessMode {
622-
return nil, status.Errorf(codes.Internal, "")
631+
return "", status.Errorf(codes.Internal, "")
623632
}
624633
} else {
625-
accessMode = am
634+
return am, nil
626635
}
627-
} else {
628-
multiWriter, _ = getMultiWriterFromCapabilities(req.GetVolumeCapabilities())
629636
}
630637

631638
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())
633652
}
634653

635654
// Validate if disk already exists
@@ -1039,31 +1058,31 @@ func (gceCS *GCEControllerServer) validateMultiZoneDisk(volumeID string, disk *g
10391058
return nil
10401059
}
10411060

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) {
10431062
if !gceCS.multiZoneVolumeHandleConfig.Enable {
1044-
return nil
1063+
return false, nil
10451064
}
10461065
if !params.MultiZoneProvisioning {
1047-
return nil
1066+
return false, nil
10481067
}
10491068

10501069
// For volume populator, we want to allow multiple RWO disks to be created
10511070
// with the same name, so they can be hydrated across multiple zones.
10521071

10531072
// We don't have support volume cloning from an existing PVC
10541073
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)
10561075
}
10571076

10581077
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)
10601079
}
10611080

10621081
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)
10641083
}
10651084

1066-
return nil
1085+
return true, nil
10671086
}
10681087

10691088
func isMultiZoneVolKey(volumeKey *meta.Key) bool {

pkg/gce-pd-csi-driver/controller_test.go

+25-22
Original file line numberDiff line numberDiff line change
@@ -1504,7 +1504,7 @@ func TestMultiZoneVolumeCreation(t *testing.T) {
15041504
expZones: []string{"us-central1-a", "us-central1-b", "us-central1-c"},
15051505
},
15061506
{
1507-
name: "err single ROX multi-zone no topology",
1507+
name: "success empty ROX multi-zone disk no content source",
15081508
req: &csi.CreateVolumeRequest{
15091509
Name: "test-name",
15101510
CapacityRange: stdCapRange,
@@ -1522,18 +1522,26 @@ func TestMultiZoneVolumeCreation(t *testing.T) {
15221522
common.ParameterKeyType: "hyperdisk-ml",
15231523
common.ParameterKeyEnableMultiZoneProvisioning: "true",
15241524
},
1525-
VolumeContentSource: &csi.VolumeContentSource{
1526-
Type: &csi.VolumeContentSource_Snapshot{
1527-
Snapshot: &csi.VolumeContentSource_SnapshotSource{
1528-
SnapshotId: testSnapshotID,
1525+
AccessibilityRequirements: &csi.TopologyRequirement{
1526+
Requisite: []*csi.Topology{
1527+
{
1528+
Segments: map[string]string{common.TopologyKeyZone: "us-central1-a"},
1529+
},
1530+
},
1531+
Preferred: []*csi.Topology{
1532+
{
1533+
Segments: map[string]string{common.TopologyKeyZone: "us-central1-a"},
1534+
},
1535+
{
1536+
Segments: map[string]string{common.TopologyKeyZone: "us-central1-b"},
15291537
},
15301538
},
15311539
},
15321540
},
1533-
expErrCode: codes.InvalidArgument,
1541+
expZones: []string{"us-central1-a", "us-central1-b"},
15341542
},
15351543
{
1536-
name: "err rwo access mode",
1544+
name: "err single ROX multi-zone no topology",
15371545
req: &csi.CreateVolumeRequest{
15381546
Name: "test-name",
15391547
CapacityRange: stdCapRange,
@@ -1543,7 +1551,7 @@ func TestMultiZoneVolumeCreation(t *testing.T) {
15431551
Mount: &csi.VolumeCapability_MountVolume{},
15441552
},
15451553
AccessMode: &csi.VolumeCapability_AccessMode{
1546-
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
1554+
Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
15471555
},
15481556
},
15491557
},
@@ -1558,23 +1566,11 @@ func TestMultiZoneVolumeCreation(t *testing.T) {
15581566
},
15591567
},
15601568
},
1561-
AccessibilityRequirements: &csi.TopologyRequirement{
1562-
Requisite: []*csi.Topology{
1563-
{
1564-
Segments: map[string]string{common.TopologyKeyZone: "us-central1-a"},
1565-
},
1566-
},
1567-
Preferred: []*csi.Topology{
1568-
{
1569-
Segments: map[string]string{common.TopologyKeyZone: "us-central1-a"},
1570-
},
1571-
},
1572-
},
15731569
},
15741570
expErrCode: codes.InvalidArgument,
15751571
},
15761572
{
1577-
name: "err no content source",
1573+
name: "err rwo access mode",
15781574
req: &csi.CreateVolumeRequest{
15791575
Name: "test-name",
15801576
CapacityRange: stdCapRange,
@@ -1584,14 +1580,21 @@ func TestMultiZoneVolumeCreation(t *testing.T) {
15841580
Mount: &csi.VolumeCapability_MountVolume{},
15851581
},
15861582
AccessMode: &csi.VolumeCapability_AccessMode{
1587-
Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
1583+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
15881584
},
15891585
},
15901586
},
15911587
Parameters: map[string]string{
15921588
common.ParameterKeyType: "hyperdisk-ml",
15931589
common.ParameterKeyEnableMultiZoneProvisioning: "true",
15941590
},
1591+
VolumeContentSource: &csi.VolumeContentSource{
1592+
Type: &csi.VolumeContentSource_Snapshot{
1593+
Snapshot: &csi.VolumeContentSource_SnapshotSource{
1594+
SnapshotId: testSnapshotID,
1595+
},
1596+
},
1597+
},
15951598
AccessibilityRequirements: &csi.TopologyRequirement{
15961599
Requisite: []*csi.Topology{
15971600
{

0 commit comments

Comments
 (0)