Skip to content

Commit 1d30b96

Browse files
Add provisionedIops for pd-extreme
1 parent 52de932 commit 1d30b96

File tree

15 files changed

+866
-59
lines changed

15 files changed

+866
-59
lines changed

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ See Github [Issues](https://github.com/kubernetes-sigs/gcp-compute-persistent-di
5858

5959
### CreateVolume Parameters
6060

61-
| Parameter | Values | Default | Description |
62-
|------------------|---------------------------|---------------|----------------------------------------------------------------------------------------------------|
63-
| type | Any PD type (see [GCP documentation](https://cloud.google.com/compute/docs/disks#disk-types)), eg `pd-ssd` `pd-balanced` | `pd-standard` | Type allows you to choose between standard Persistent Disks or Solid State Drive Persistent Disks |
64-
| replication-type | `none` OR `regional-pd` | `none` | Replication type allows you to choose between Zonal Persistent Disks or Regional Persistent Disks |
65-
| disk-encryption-kms-key | Fully qualified resource identifier for the key to use to encrypt new disks. | Empty string. | Encrypt disk using Customer Managed Encryption Key (CMEK). See [GKE Docs](https://cloud.google.com/kubernetes-engine/docs/how-to/using-cmek#create_a_cmek_protected_attached_disk) for details. |
66-
| labels | `key1=value1,key2=value2` | | Labels allow you to assign custom [GCE Disk labels](https://cloud.google.com/compute/docs/labeling-resources). |
61+
| Parameter | Values | Default | Description |
62+
|-----------------------------|---------------------------|---------------|----------------------------------------------------------------------------------------------------|
63+
| type | Any PD type (see [GCP documentation](https://cloud.google.com/compute/docs/disks#disk-types)), eg `pd-ssd` `pd-balanced` | `pd-standard` | Type allows you to choose between standard Persistent Disks or Solid State Drive Persistent Disks |
64+
| replication-type | `none` OR `regional-pd` | `none` | Replication type allows you to choose between Zonal Persistent Disks or Regional Persistent Disks |
65+
| disk-encryption-kms-key | Fully qualified resource identifier for the key to use to encrypt new disks. | Empty string. | Encrypt disk using Customer Managed Encryption Key (CMEK). See [GKE Docs](https://cloud.google.com/kubernetes-engine/docs/how-to/using-cmek#create_a_cmek_protected_attached_disk) for details. |
66+
| labels | `key1=value1,key2=value2` | | Labels allow you to assign custom [GCE Disk labels](https://cloud.google.com/compute/docs/labeling-resources). |
67+
| 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. |
68+
6769

6870
### Topology
6971

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ require (
3131

3232
require google.golang.org/protobuf v1.26.0
3333

34+
require (
35+
google.golang.org/protobuf v1.26.0
36+
k8s.io/cloud-provider v0.22.0
37+
)
38+
3439
require (
3540
github.com/Microsoft/go-winio v0.4.16 // indirect
3641
github.com/beorn7/perks v1.0.1 // indirect

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,7 @@ k8s.io/apiserver v0.22.0/go.mod h1:04kaIEzIQrTGJ5syLppQWvpkLJXQtJECHmae+ZGc/nc=
11971197
k8s.io/cli-runtime v0.22.0/go.mod h1:An6zELQ7udUI0GaXvkuMqyopPA14dIgNqpH8cZu1vig=
11981198
k8s.io/client-go v0.22.0 h1:sD6o9O6tCwUKCENw8v+HFsuAbq2jCu8cWC61/ydwA50=
11991199
k8s.io/client-go v0.22.0/go.mod h1:GUjIuXR5PiEv/RVK5OODUsm6eZk7wtSWZSaSJbpFdGg=
1200+
k8s.io/cloud-provider v0.22.0 h1:eK0swLQ1TZCLefRbgwEo/ZS4ZDo6FkOJDkDIBITshyw=
12001201
k8s.io/cloud-provider v0.22.0/go.mod h1:UsQNOxrStwOXoDfVNgEbKgcQt2BYuHGKobixm0zKTis=
12011202
k8s.io/cluster-bootstrap v0.22.0/go.mod h1:VeZXiGfH+yfnC2KtvkSwNTAqahg6yiCV/szbWpoI+3k=
12021203
k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=

pkg/common/parameters.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ import (
2323

2424
const (
2525
// Parameters for StorageClass
26-
ParameterKeyType = "type"
27-
ParameterKeyReplicationType = "replication-type"
28-
ParameterKeyDiskEncryptionKmsKey = "disk-encryption-kms-key"
29-
ParameterKeyLabels = "labels"
26+
ParameterKeyType = "type"
27+
ParameterKeyReplicationType = "replication-type"
28+
ParameterKeyDiskEncryptionKmsKey = "disk-encryption-kms-key"
29+
ParameterKeyLabels = "labels"
30+
ParameterKeyProvisionedIOPSOnCreate = "provisioned-iops-on-create"
3031

3132
// Parameters for VolumeSnapshotClass
3233
ParameterKeyStorageLocations = "storage-locations"
@@ -75,6 +76,9 @@ type DiskParameters struct {
7576
// Values: {map[string]string}
7677
// Default: ""
7778
Labels map[string]string
79+
// Values: {int64}
80+
// Default: none
81+
ProvisionedIOPSOnCreate int64
7882
}
7983

8084
// SnapshotParameters contains normalized and defaulted parameters for snapshots
@@ -134,6 +138,12 @@ func ExtractAndDefaultParameters(parameters map[string]string, driverName string
134138
for labelKey, labelValue := range paramLabels {
135139
p.Labels[labelKey] = labelValue
136140
}
141+
case ParameterKeyProvisionedIOPSOnCreate:
142+
paramProvisionedIOPSOnCreate, err := ConvertGiBStringToInt64(v)
143+
if err != nil {
144+
return p, fmt.Errorf("parameters contain invalid provisionedIOPSOnCreate parameter: %w", err)
145+
}
146+
p.ProvisionedIOPSOnCreate = paramProvisionedIOPSOnCreate
137147
default:
138148
return p, fmt.Errorf("parameters contains invalid option %q", k)
139149
}

pkg/common/parameters_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,22 @@ func TestExtractAndDefaultParameters(t *testing.T) {
7474
},
7575
},
7676
},
77+
{
78+
name: "values from parameters, checking pd-extreme",
79+
parameters: map[string]string{ParameterKeyType: "pd-extreme", ParameterKeyReplicationType: "none", ParameterKeyDiskEncryptionKmsKey: "foo/key", ParameterKeyLabels: "key1=value1,key2=value2", ParameterKeyProvisionedIOPSOnCreate: "10000Gi"},
80+
labels: map[string]string{},
81+
expectParams: DiskParameters{
82+
DiskType: "pd-extreme",
83+
ReplicationType: "none",
84+
DiskEncryptionKMSKey: "foo/key",
85+
Tags: map[string]string{},
86+
Labels: map[string]string{
87+
"key1": "value1",
88+
"key2": "value2",
89+
},
90+
ProvisionedIOPSOnCreate: 10000,
91+
},
92+
},
7793
{
7894
name: "values from parameters, checking balanced pd",
7995
parameters: map[string]string{ParameterKeyType: "pd-balanced", ParameterKeyReplicationType: "regional-pd", ParameterKeyDiskEncryptionKmsKey: "foo/key"},

pkg/common/utils.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import (
2222
"strings"
2323

2424
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
25+
"k8s.io/apimachinery/pkg/api/resource"
2526
"k8s.io/apimachinery/pkg/util/sets"
27+
volumehelpers "k8s.io/cloud-provider/volume/helpers"
2628
)
2729

2830
const (
@@ -257,14 +259,13 @@ func ValidateSnapshotType(snapshotType string) error {
257259
}
258260
}
259261

260-
// ParseMachineType returns an extracted machineType from a URL, or empty if not found.
261-
// machineTypeUrl: Full or partial URL of the machine type resource, in the format:
262-
//
263-
// zones/zone/machineTypes/machine-type
264-
func ParseMachineType(machineTypeUrl string) (string, error) {
265-
machineType := machineTypeRegex.FindStringSubmatch(machineTypeUrl)
266-
if machineType == nil {
267-
return "", fmt.Errorf("failed to parse machineTypeUrl. Expected suffix: zones/{zone}/machineTypes/{machine-type}. Got: %s", machineTypeUrl)
262+
// ConvertGiBStringToInt64 converts a GiB string to int64
263+
func ConvertGiBStringToInt64(str string) (int64, error) {
264+
// Verify regex before
265+
match, _ := regexp.MatchString("^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$", str)
266+
if !match {
267+
return 0, fmt.Errorf("invalid string %s", str)
268268
}
269-
return machineType[1], nil
269+
quantity := resource.MustParse(str)
270+
return volumehelpers.RoundUpToGiB(quantity)
270271
}

pkg/common/utils_test.go

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -578,55 +578,91 @@ func TestSnapshotStorageLocations(t *testing.T) {
578578
}
579579
}
580580

581-
func TestParseMachineType(t *testing.T) {
581+
func TestConvertGiBStringToInt64(t *testing.T) {
582582
tests := []struct {
583-
desc string
584-
inputMachineTypeUrl string
585-
expectedMachineType string
586-
expectError bool
583+
desc string
584+
inputStr string
585+
expInt64 int64
586+
expectError bool
587587
}{
588588
{
589-
desc: "full URL machine type",
590-
inputMachineTypeUrl: "https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/machineTypes/c3-highcpu-4",
591-
expectedMachineType: "c3-highcpu-4",
589+
"valid number string",
590+
"10000",
591+
1,
592+
false,
592593
},
593594
{
594-
desc: "partial URL machine type",
595-
inputMachineTypeUrl: "zones/us-central1-c/machineTypes/n2-standard-4",
596-
expectedMachineType: "n2-standard-4",
595+
"round Ki to GiB",
596+
"1000Ki",
597+
1,
598+
false,
597599
},
598600
{
599-
desc: "custom partial URL machine type",
600-
inputMachineTypeUrl: "zones/us-central1-c/machineTypes/e2-custom-2-4096",
601-
expectedMachineType: "e2-custom-2-4096",
601+
"round k to GiB",
602+
"1000k",
603+
1,
604+
false,
602605
},
603606
{
604-
desc: "incorrect URL",
605-
inputMachineTypeUrl: "https://www.googleapis.com/compute/v1/projects/psch-gke-dev/zones/us-central1-c",
606-
expectError: true,
607+
"round Mi to GiB",
608+
"1000Mi",
609+
1,
610+
false,
607611
},
608612
{
609-
desc: "incorrect partial URL",
610-
inputMachineTypeUrl: "zones/us-central1-c/machineTypes/",
611-
expectError: true,
613+
"round M to GiB",
614+
"1000M",
615+
1,
616+
false,
617+
},
618+
{
619+
"round G to GiB",
620+
"1000G",
621+
932,
622+
false,
623+
},
624+
{
625+
"round Gi to GiB",
626+
"10000Gi",
627+
10000,
628+
false,
612629
},
613630
{
614-
desc: "missing zone",
615-
inputMachineTypeUrl: "zones//machineTypes/n2-standard-4",
616-
expectError: true,
631+
"round decimal to GiB",
632+
"1.2Gi",
633+
2,
634+
false,
635+
},
636+
{
637+
"round big value to GiB",
638+
"8191Pi",
639+
8588886016,
640+
false,
641+
},
642+
{
643+
"invalid empty string",
644+
"",
645+
10000,
646+
true,
647+
},
648+
{
649+
"invalid string",
650+
"ew%65",
651+
10000,
652+
true,
617653
},
618654
}
619655
for _, tc := range tests {
620656
t.Run(tc.desc, func(t *testing.T) {
621-
actualMachineFamily, err := ParseMachineType(tc.inputMachineTypeUrl)
657+
actualInt64, err := ConvertGiBStringToInt64(tc.inputStr)
622658
if err != nil && !tc.expectError {
623-
t.Errorf("Got error %v parsing machine type %s; expect no error", err, tc.inputMachineTypeUrl)
659+
t.Errorf("Got error %v converting string to int64 %s; expect no error", err, tc.inputStr)
624660
}
625661
if err == nil && tc.expectError {
626-
t.Errorf("Got no error parsing machine type %s; expect an error", tc.inputMachineTypeUrl)
662+
t.Errorf("Got no error converting string to int64 %s; expect an error", tc.inputStr)
627663
}
628-
if err == nil && actualMachineFamily != tc.expectedMachineType {
629-
t.Errorf("Got %s parsing machine type; expect %s", actualMachineFamily, tc.expectedMachineType)
664+
if err == nil && actualInt64 != tc.expInt64 {
665+
t.Errorf("Got %d for converting string to int64; expect %d", actualInt64, tc.expInt64)
630666
}
631667
})
632668
}

pkg/gce-cloud-provider/compute/fake-gce.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,14 @@ func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, project string,
196196
}
197197

198198
computeDisk := &computev1.Disk{
199-
Name: volKey.Name,
200-
SizeGb: common.BytesToGbRoundUp(capBytes),
201-
Description: "Disk created by GCE-PD CSI Driver",
202-
Type: cloud.GetDiskTypeURI(project, volKey, params.DiskType),
203-
SourceDiskId: volumeContentSourceVolumeID,
204-
Status: cloud.mockDiskStatus,
205-
Labels: params.Labels,
199+
Name: volKey.Name,
200+
SizeGb: common.BytesToGbRoundUp(capBytes),
201+
Description: "Disk created by GCE-PD CSI Driver",
202+
Type: cloud.GetDiskTypeURI(project, volKey, params.DiskType),
203+
SourceDiskId: volumeContentSourceVolumeID,
204+
Status: cloud.mockDiskStatus,
205+
Labels: params.Labels,
206+
ProvisionedIops: params.ProvisionedIOPSOnCreate,
206207
}
207208

208209
if snapshotID != "" {

pkg/gce-cloud-provider/compute/gce-compute.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,12 @@ func (cloud *CloudProvider) insertRegionalDisk(
443443
}
444444

445445
diskToCreate := &computev1.Disk{
446-
Name: volKey.Name,
447-
SizeGb: common.BytesToGbRoundUp(capBytes),
448-
Description: description,
449-
Type: cloud.GetDiskTypeURI(cloud.project, volKey, params.DiskType),
450-
Labels: params.Labels,
446+
Name: volKey.Name,
447+
SizeGb: common.BytesToGbRoundUp(capBytes),
448+
Description: description,
449+
Type: cloud.GetDiskTypeURI(cloud.project, volKey, params.DiskType),
450+
Labels: params.Labels,
451+
ProvisionedIops: params.ProvisionedIOPSOnCreate,
451452
}
452453
if snapshotID != "" {
453454
_, snapshotType, _, err := common.SnapshotIDToProjectKey(snapshotID)

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,31 @@ func TestCreateVolumeArguments(t *testing.T) {
793793
},
794794
expErrCode: codes.InvalidArgument,
795795
},
796+
{
797+
name: "success with provisionedIops parameter",
798+
req: &csi.CreateVolumeRequest{
799+
Name: name,
800+
CapacityRange: stdCapRange,
801+
VolumeCapabilities: stdVolCaps,
802+
Parameters: map[string]string{"labels": "key1=value1,key2=value2", "provisioned-iops-on-create": "10000"},
803+
},
804+
expVol: &csi.Volume{
805+
CapacityBytes: common.GbToBytes(20),
806+
VolumeId: testVolumeID,
807+
VolumeContext: nil,
808+
AccessibleTopology: stdTopology,
809+
},
810+
},
811+
{
812+
name: "fail with malformed provisionedIops parameter",
813+
req: &csi.CreateVolumeRequest{
814+
Name: name,
815+
CapacityRange: stdCapRange,
816+
VolumeCapabilities: stdVolCaps,
817+
Parameters: map[string]string{"labels": "key1=value1,key2=value2", "provisioned-iops-on-create": "dsfo3"},
818+
},
819+
expErrCode: codes.InvalidArgument,
820+
},
796821
}
797822

798823
// Run test cases

0 commit comments

Comments
 (0)