Skip to content

Commit 84af6e9

Browse files
authored
Merge pull request #2089 from julianKatz/enable-disk-topology-with-StorageClass-parameter
Only add disk support topologies if StorageClass parameter is true
2 parents 55839ca + 5ad3127 commit 84af6e9

File tree

8 files changed

+193
-40
lines changed

8 files changed

+193
-40
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ See Github [Issues](https://github.com/kubernetes-sigs/gcp-compute-persistent-di
6161
| provisioned-iops-on-create | string (int64 format). Values typically between 10,000 and 120,000 | | Indicates how many IOPS to provision for the disk. See the [Extreme persistent disk documentation](https://cloud.google.com/compute/docs/disks/extreme-persistent-disk) for details, including valid ranges for IOPS. |
6262
| provisioned-throughput-on-create | string (int64 format). Values typically between 1 and 7,124 mb per second | | Indicates how much throughput to provision for the disk. See the [hyperdisk documentation]([TBD](https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/hyperdisk#create)) for details, including valid ranges for throughput. |
6363
| resource-tags | `<parent_id1>/<tag_key1>/<tag_value1>,<parent_id2>/<tag_key2>/<tag_value2>` | | Resource tags allow you to attach user-defined tags to each Compute Disk, Image and Snapshot. See [Tags overview](https://cloud.google.com/resource-manager/docs/tags/tags-overview), [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing). |
64+
| use-allowed-disk-topologies | `true` or `false` | `false` | Allows the use of specific disk topologies for provisioning. Must be used in combination with the `--disk-topology=true` flag on PDCSI binary to yield disk support labels in PV NodeAffinity blocks. |
6465

6566
### Topology
6667

cmd/gce-pd-csi-driver/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ var (
9494

9595
extraTagsStr = flag.String("extra-tags", "", "Extra tags to attach to each Compute Disk, Image, Snapshot created. It is a comma separated list of parent id, key and value like '<parent_id1>/<tag_key1>/<tag_value1>,...,<parent_idN>/<tag_keyN>/<tag_valueN>'. parent_id is the Organization or the Project ID or Project name where the tag key and the tag value resources exist. A maximum of 50 tags bindings is allowed for a resource. See https://cloud.google.com/resource-manager/docs/tags/tags-overview, https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing for details")
9696

97-
diskTopology = flag.Bool("disk-topology", false, "If set to true, the driver will add a disk-type.gke.io/[some-disk-type] topology label to the Topologies returned in CreateVolumeResponse.")
97+
diskTopology = flag.Bool("disk-topology", false, "If set to true, the driver will add a disk-type.gke.io/[disk-type] topology label when the StorageClass has the use-allowed-disk-topology parameter set to true. That topology label is included in the Topologies returned in CreateVolumeResponse. This flag is disabled by default.")
9898

9999
version string
100100
)

pkg/common/parameters.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"fmt"
2121
"strconv"
2222
"strings"
23+
24+
"k8s.io/klog/v2"
2325
)
2426

2527
const (
@@ -36,6 +38,7 @@ const (
3638
ParameterAvailabilityClass = "availability-class"
3739
ParameterKeyEnableConfidentialCompute = "enable-confidential-storage"
3840
ParameterKeyStoragePools = "storage-pools"
41+
ParameterKeyUseAllowedDiskTopology = "use-allowed-disk-topology"
3942

4043
// Parameters for Data Cache
4144
ParameterKeyDataCacheSize = "data-cache-size"
@@ -132,6 +135,9 @@ type DiskParameters struct {
132135
// Values: READ_WRITE_SINGLE, READ_ONLY_MANY, READ_WRITE_MANY
133136
// Default: READ_WRITE_SINGLE
134137
AccessMode string
138+
// Values {}
139+
// Default: false
140+
UseAllowedDiskTopology bool
135141
}
136142

137143
func (dp *DiskParameters) IsRegional() bool {
@@ -160,6 +166,7 @@ type ParameterProcessor struct {
160166
EnableStoragePools bool
161167
EnableMultiZone bool
162168
EnableHdHA bool
169+
EnableDiskTopology bool
163170
}
164171

165172
type ModifyVolumeParameters struct {
@@ -319,6 +326,19 @@ func (pp *ParameterProcessor) ExtractAndDefaultParameters(parameters map[string]
319326
if v != "" {
320327
p.AccessMode = v
321328
}
329+
case ParameterKeyUseAllowedDiskTopology:
330+
if !pp.EnableDiskTopology {
331+
klog.Warningf("parameters contains invalid option %q when disk topology is not enabled", ParameterKeyUseAllowedDiskTopology)
332+
continue
333+
}
334+
335+
paramUseAllowedDiskTopology, err := ConvertStringToBool(v)
336+
if err != nil {
337+
klog.Warningf("failed to convert %s parameter with value %q to bool: %v", ParameterKeyUseAllowedDiskTopology, v, err)
338+
continue
339+
}
340+
341+
p.UseAllowedDiskTopology = paramUseAllowedDiskTopology
322342
default:
323343
return p, d, fmt.Errorf("parameters contains invalid option %q", k)
324344
}

pkg/common/parameters_test.go

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func TestExtractAndDefaultParameters(t *testing.T) {
3232
enableDataCache bool
3333
enableMultiZone bool
3434
enableHdHA bool
35+
enableDiskTopology bool
3536
extraTags map[string]string
3637
expectParams DiskParameters
3738
expectDataCacheParams DataCacheParameters
@@ -42,12 +43,13 @@ func TestExtractAndDefaultParameters(t *testing.T) {
4243
parameters: map[string]string{},
4344
labels: map[string]string{},
4445
expectParams: DiskParameters{
45-
DiskType: "pd-standard",
46-
ReplicationType: "none",
47-
DiskEncryptionKMSKey: "",
48-
Tags: map[string]string{},
49-
Labels: map[string]string{},
50-
ResourceTags: map[string]string{},
46+
DiskType: "pd-standard",
47+
ReplicationType: "none",
48+
DiskEncryptionKMSKey: "",
49+
Tags: map[string]string{},
50+
Labels: map[string]string{},
51+
ResourceTags: map[string]string{},
52+
UseAllowedDiskTopology: false,
5153
},
5254
},
5355
{
@@ -464,6 +466,53 @@ func TestExtractAndDefaultParameters(t *testing.T) {
464466
Labels: map[string]string{},
465467
},
466468
},
469+
{
470+
// Disk topology feature shouldn't cause parameter parsing to fail, even when misconfigured.
471+
name: "useAllowedDiskTopology specified, disk topology feature disabled",
472+
parameters: map[string]string{
473+
ParameterKeyUseAllowedDiskTopology: "true", // Correct type: boolean string.
474+
},
475+
labels: map[string]string{},
476+
expectParams: DiskParameters{
477+
DiskType: "pd-standard",
478+
ReplicationType: "none",
479+
DiskEncryptionKMSKey: "",
480+
Tags: map[string]string{},
481+
Labels: map[string]string{},
482+
ResourceTags: map[string]string{},
483+
},
484+
},
485+
{
486+
// Disk topology feature shouldn't cause parameter parsing to fail, even when misconfigured.
487+
name: "useAllowedDiskTopology specified, wrong type",
488+
enableDiskTopology: true,
489+
parameters: map[string]string{
490+
ParameterKeyUseAllowedDiskTopology: "123", // Incorrect type: number.
491+
},
492+
labels: map[string]string{},
493+
expectParams: DiskParameters{
494+
DiskType: "pd-standard",
495+
ReplicationType: "none",
496+
DiskEncryptionKMSKey: "",
497+
Tags: map[string]string{},
498+
Labels: map[string]string{},
499+
ResourceTags: map[string]string{},
500+
},
501+
},
502+
{
503+
name: "useAllowedDiskTopology specified as valid true boolean",
504+
parameters: map[string]string{ParameterKeyUseAllowedDiskTopology: "true"},
505+
enableDiskTopology: true,
506+
expectParams: DiskParameters{
507+
DiskType: "pd-standard",
508+
ReplicationType: "none",
509+
DiskEncryptionKMSKey: "",
510+
Tags: map[string]string{},
511+
Labels: map[string]string{},
512+
ResourceTags: map[string]string{},
513+
UseAllowedDiskTopology: true,
514+
},
515+
},
467516
}
468517

469518
for _, tc := range tests {
@@ -473,6 +522,7 @@ func TestExtractAndDefaultParameters(t *testing.T) {
473522
EnableStoragePools: tc.enableStoragePools,
474523
EnableMultiZone: tc.enableMultiZone,
475524
EnableHdHA: tc.enableHdHA,
525+
EnableDiskTopology: tc.enableDiskTopology,
476526
}
477527
p, d, err := pp.ExtractAndDefaultParameters(tc.parameters, tc.labels, tc.enableDataCache, tc.extraTags)
478528
if gotErr := err != nil; gotErr != tc.expectErr {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,7 @@ func (gceCS *GCEControllerServer) parameterProcessor() *common.ParameterProcesso
13391339
EnableStoragePools: gceCS.enableStoragePools,
13401340
EnableMultiZone: gceCS.multiZoneVolumeHandleConfig.Enable,
13411341
EnableHdHA: gceCS.enableHdHA,
1342+
EnableDiskTopology: gceCS.EnableDiskTopology,
13421343
}
13431344
}
13441345

@@ -2418,7 +2419,11 @@ func (gceCS *GCEControllerServer) generateCreateVolumeResponseWithVolumeId(disk
24182419
},
24192420
}
24202421

2421-
if gceCS.EnableDiskTopology {
2422+
// The addition of the disk type label requires both the feature to be
2423+
// enabled on the PDCSI binary via the `--disk-topology=true` flag AND
2424+
// the StorageClass to have the `use-allowed-disk-topology` parameter
2425+
// set to `true`.
2426+
if gceCS.EnableDiskTopology && params.UseAllowedDiskTopology {
24222427
top.Segments[common.DiskTypeLabelKey(params.DiskType)] = "true"
24232428
}
24242429

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

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,13 +1349,43 @@ func TestCreateVolumeArguments(t *testing.T) {
13491349
},
13501350
// Disk Topology Enabled tests
13511351
{
1352-
name: "success with disk topology enabled",
1352+
name: "success with disk topology enabled and StorageClass useAllowedDiskTopology false",
13531353
req: &csi.CreateVolumeRequest{
13541354
Name: "test-name",
13551355
CapacityRange: stdCapRange,
13561356
VolumeCapabilities: stdVolCaps,
1357-
Parameters: stdParams,
1357+
Parameters: mergeParameters(
1358+
stdParams,
1359+
map[string]string{common.ParameterKeyUseAllowedDiskTopology: "false"},
1360+
),
13581361
},
1362+
enableDiskTopology: true,
1363+
expVol: &csi.Volume{
1364+
CapacityBytes: common.GbToBytes(20),
1365+
VolumeId: testVolumeID,
1366+
VolumeContext: nil,
1367+
AccessibleTopology: []*csi.Topology{
1368+
{
1369+
Segments: map[string]string{
1370+
common.TopologyKeyZone: zone,
1371+
// Disk not type not included since useAllowedDiskTopology is false
1372+
},
1373+
},
1374+
},
1375+
},
1376+
},
1377+
{
1378+
name: "success with disk topology enabled and StorageClass useAllowedDiskTopology true",
1379+
req: &csi.CreateVolumeRequest{
1380+
Name: "test-name",
1381+
CapacityRange: stdCapRange,
1382+
VolumeCapabilities: stdVolCaps,
1383+
Parameters: mergeParameters(
1384+
stdParams,
1385+
map[string]string{common.ParameterKeyUseAllowedDiskTopology: "true"},
1386+
),
1387+
},
1388+
enableDiskTopology: true,
13591389
expVol: &csi.Volume{
13601390
CapacityBytes: common.GbToBytes(20),
13611391
VolumeId: testVolumeID,
@@ -1370,46 +1400,46 @@ func TestCreateVolumeArguments(t *testing.T) {
13701400
},
13711401
},
13721402
},
1373-
enableDiskTopology: true,
13741403
},
13751404
}
13761405

13771406
// Run test cases
13781407
for _, tc := range testCases {
1379-
t.Logf("test case: %s", tc.name)
1380-
1381-
// Setup new driver each time so no interference
1382-
args := &GCEControllerServerArgs{EnableDiskTopology: tc.enableDiskTopology}
1383-
gceDriver := initGCEDriver(t, nil, args)
1384-
gceDriver.cs.enableStoragePools = tc.enableStoragePools
1408+
t.Run(tc.name, func(t *testing.T) {
1409+
// Setup new driver each time so no interference
1410+
args := &GCEControllerServerArgs{EnableDiskTopology: tc.enableDiskTopology}
1411+
gceDriver := initGCEDriver(t, nil, args)
1412+
gceDriver.cs.enableStoragePools = tc.enableStoragePools
13851413

1386-
// Start Test
1387-
resp, err := gceDriver.cs.CreateVolume(context.Background(), tc.req)
1388-
if err != nil {
1389-
serverError, ok := status.FromError(err)
1390-
if !ok {
1391-
t.Fatalf("Could not get error status code from err: %v", serverError)
1414+
// Start Test
1415+
resp, err := gceDriver.cs.CreateVolume(context.Background(), tc.req)
1416+
if err != nil {
1417+
serverError, ok := status.FromError(err)
1418+
if !ok {
1419+
t.Fatalf("Could not get error status code from err: %v", serverError)
1420+
}
1421+
if serverError.Code() != tc.expErrCode {
1422+
t.Fatalf("Expected error code: %v, got: %v. err : %v", tc.expErrCode, serverError.Code(), err)
1423+
}
1424+
return
13921425
}
1393-
if serverError.Code() != tc.expErrCode {
1394-
t.Fatalf("Expected error code: %v, got: %v. err : %v", tc.expErrCode, serverError.Code(), err)
1426+
1427+
t.Logf("ErrorCode: %v", err)
1428+
if tc.expErrCode != codes.OK {
1429+
t.Fatalf("Expected error: %v, got no error", tc.expErrCode)
13951430
}
1396-
continue
1397-
}
1398-
t.Logf("ErroCode: %v", err)
1399-
if tc.expErrCode != codes.OK {
1400-
t.Fatalf("Expected error: %v, got no error", tc.expErrCode)
1401-
}
14021431

1403-
// Make sure responses match
1404-
vol := resp.GetVolume()
1405-
if vol == nil {
1406-
// If one is nil but not both
1407-
t.Fatalf("Expected volume %v, got nil volume", tc.expVol)
1408-
}
1432+
// Make sure responses match
1433+
vol := resp.GetVolume()
1434+
if vol == nil {
1435+
// If one is nil but not both
1436+
t.Fatalf("Expected volume %v, got nil volume", tc.expVol)
1437+
}
14091438

1410-
if diff := cmp.Diff(vol, tc.expVol, protocmp.Transform()); diff != "" {
1411-
t.Errorf("unexpected diff (-vol, +expVol): \n%s", diff)
1412-
}
1439+
if diff := cmp.Diff(vol, tc.expVol, protocmp.Transform()); diff != "" {
1440+
t.Errorf("unexpected diff (-vol, +expVol): \n%s", diff)
1441+
}
1442+
})
14131443
}
14141444
}
14151445

@@ -5818,3 +5848,14 @@ func googleapiErrContainsReason(err *googleapi.Error, reason string) bool {
58185848
}
58195849
return false
58205850
}
5851+
5852+
func mergeParameters(a map[string]string, b map[string]string) map[string]string {
5853+
merged := make(map[string]string)
5854+
for k, v := range a {
5855+
merged[k] = v
5856+
}
5857+
for k, v := range b {
5858+
merged[k] = v
5859+
}
5860+
return merged
5861+
}

test/e2e/tests/single_zone_e2e_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,41 @@ var _ = Describe("GCE PD CSI Driver", func() {
283283
Expect(err).To(BeNil(), "Could not find disk in correct zone")
284284
}
285285
})
286+
287+
It("Should create a volume with allowed disk topology and confirm disk support label", func() {
288+
Expect(testContexts).ToNot(BeEmpty())
289+
testContext := getRandomTestContext()
290+
291+
volName := testNamePrefix + string(uuid.NewUUID())
292+
params := map[string]string{
293+
"type": hdbDiskType,
294+
// Required to enable the disk topology feature.
295+
"use-allowed-disk-topology": "true",
296+
}
297+
298+
topReq := &csi.TopologyRequirement{
299+
Requisite: []*csi.Topology{
300+
{
301+
Segments: map[string]string{common.TopologyKeyZone: "us-central1-c"},
302+
},
303+
},
304+
}
305+
306+
volume, err := testContext.Client.CreateVolume(volName, params, defaultSizeGb, topReq, nil)
307+
Expect(err).To(BeNil(), "Failed to create volume")
308+
defer func() {
309+
err = testContext.Client.DeleteVolume(volume.VolumeId)
310+
Expect(err).To(BeNil(), "Failed to delete volume")
311+
}()
312+
313+
// Confirm that the topologies include a disk support label
314+
Expect(volume.AccessibleTopology).ToNot(BeEmpty(), "Volume should have accessible topologies")
315+
Expect(volume.AccessibleTopology).To(HaveLen(1), "Expected exactly one accessible topology") // Zonal clusters have a single Topology.
316+
segments := volume.AccessibleTopology[0].Segments
317+
Expect(segments).To(HaveKeyWithValue(common.TopologyKeyZone, "us-central1-c"), "Topology should include zone segment with value 'us-central1-c'")
318+
Expect(segments).To(HaveKeyWithValue(common.DiskTypeLabelKey(hdbDiskType), "true"), "Topology should include disk type label with value 'true'")
319+
})
320+
286321
// TODO(hime): Enable this test once all release branches contain the fix from PR#1708.
287322
// It("Should return InvalidArgument when disk size exceeds limit", func() {
288323
// // If this returns a different error code (like Unknown), the error wrapping logic in #1708 has regressed.

test/e2e/utils/utils.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func GCEClientAndDriverSetup(instance *remote.InstanceInfo, driverConfig DriverC
7171
"--allow-hdha-provisioning",
7272
"--device-in-use-timeout=10s", // Set lower than the usual value to expedite tests
7373
fmt.Sprintf("--fallback-requisite-zones=%s", strings.Join(driverConfig.Zones, ",")),
74+
"--disk-topology=true",
7475
}
7576

7677
extra_flags = append(extra_flags, fmt.Sprintf("--node-name=%s", utilcommon.TestNode))

0 commit comments

Comments
 (0)