Skip to content

Commit 900f5f7

Browse files
authored
Merge pull request #1101 from sunnylovestiramisu/hyperdisk
Add provisionedThroughput for hyperdisk-throughput
2 parents f7cbee7 + ba88504 commit 900f5f7

File tree

10 files changed

+6632
-1375
lines changed

10 files changed

+6632
-1375
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ See Github [Issues](https://github.com/kubernetes-sigs/gcp-compute-persistent-di
6565
| 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. |
6666
| labels | `key1=value1,key2=value2` | | Labels allow you to assign custom [GCE Disk labels](https://cloud.google.com/compute/docs/labeling-resources). |
6767
| 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-
68+
| 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) for details, including valid ranges for throughput. |
6969

7070
### Topology
7171

Diff for: pkg/common/parameters.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ import (
2323

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

3233
// Parameters for VolumeSnapshotClass
3334
ParameterKeyStorageLocations = "storage-locations"
@@ -84,6 +85,9 @@ type DiskParameters struct {
8485
// Values: {int64}
8586
// Default: none
8687
ProvisionedIOPSOnCreate int64
88+
// Values: {int64}
89+
// Default: none
90+
ProvisionedThroughputOnCreate int64
8791
}
8892

8993
// SnapshotParameters contains normalized and defaulted parameters for snapshots
@@ -153,6 +157,12 @@ func ExtractAndDefaultParameters(parameters map[string]string, driverName string
153157
return p, fmt.Errorf("parameters contain invalid provisionedIOPSOnCreate parameter: %w", err)
154158
}
155159
p.ProvisionedIOPSOnCreate = paramProvisionedIOPSOnCreate
160+
case ParameterKeyProvisionedThroughputOnCreate:
161+
paramProvisionedThroughputOnCreate, err := ConvertMiBStringToInt64(v)
162+
if err != nil {
163+
return p, fmt.Errorf("parameters contain invalid provisionedThroughputOnCreate parameter: %w", err)
164+
}
165+
p.ProvisionedThroughputOnCreate = paramProvisionedThroughputOnCreate
156166
default:
157167
return p, fmt.Errorf("parameters contains invalid option %q", k)
158168
}

Diff for: pkg/common/parameters_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,22 @@ func TestExtractAndDefaultParameters(t *testing.T) {
9090
ProvisionedIOPSOnCreate: 10000,
9191
},
9292
},
93+
{
94+
name: "values from parameters, checking hyperdisk-throughput",
95+
parameters: map[string]string{ParameterKeyType: "hyperdisk-throughput", ParameterKeyReplicationType: "none", ParameterKeyDiskEncryptionKmsKey: "foo/key", ParameterKeyLabels: "key1=value1,key2=value2", ParameterKeyProvisionedThroughputOnCreate: "1000Mi"},
96+
labels: map[string]string{},
97+
expectParams: DiskParameters{
98+
DiskType: "hyperdisk-throughput",
99+
ReplicationType: "none",
100+
DiskEncryptionKMSKey: "foo/key",
101+
Tags: map[string]string{},
102+
Labels: map[string]string{
103+
"key1": "value1",
104+
"key2": "value2",
105+
},
106+
ProvisionedThroughputOnCreate: 1000,
107+
},
108+
},
93109
{
94110
name: "values from parameters, checking balanced pd",
95111
parameters: map[string]string{ParameterKeyType: "pd-balanced", ParameterKeyReplicationType: "regional-pd", ParameterKeyDiskEncryptionKmsKey: "foo/key"},

Diff for: pkg/common/utils.go

+11
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,14 @@ func ConvertGiBStringToInt64(str string) (int64, error) {
261261
quantity := resource.MustParse(str)
262262
return volumehelpers.RoundUpToGiB(quantity)
263263
}
264+
265+
// ConvertMiBStringToInt64 converts a GiB string to int64
266+
func ConvertMiBStringToInt64(str string) (int64, error) {
267+
// Verify regex before
268+
match, _ := regexp.MatchString("^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$", str)
269+
if !match {
270+
return 0, fmt.Errorf("invalid string %s", str)
271+
}
272+
quantity := resource.MustParse(str)
273+
return volumehelpers.RoundUpToMiB(quantity)
274+
}

Diff for: pkg/common/utils_test.go

+90
Original file line numberDiff line numberDiff line change
@@ -667,3 +667,93 @@ func TestConvertGiBStringToInt64(t *testing.T) {
667667
})
668668
}
669669
}
670+
671+
func TestConvertMiBStringToInt64(t *testing.T) {
672+
tests := []struct {
673+
desc string
674+
inputStr string
675+
expInt64 int64
676+
expectError bool
677+
}{
678+
{
679+
"valid number string",
680+
"10000",
681+
1,
682+
false,
683+
},
684+
{
685+
"round Ki to MiB",
686+
"1000Ki",
687+
1,
688+
false,
689+
},
690+
{
691+
"round k to MiB",
692+
"1000k",
693+
1,
694+
false,
695+
},
696+
{
697+
"round Mi to MiB",
698+
"1000Mi",
699+
1000,
700+
false,
701+
},
702+
{
703+
"round M to MiB",
704+
"1000M",
705+
954,
706+
false,
707+
},
708+
{
709+
"round G to MiB",
710+
"1000G",
711+
953675,
712+
false,
713+
},
714+
{
715+
"round Gi to MiB",
716+
"10000Gi",
717+
10240000,
718+
false,
719+
},
720+
{
721+
"round decimal to MiB",
722+
"1.2Gi",
723+
1229,
724+
false,
725+
},
726+
{
727+
"round big value to MiB",
728+
"8191Pi",
729+
8795019280384,
730+
false,
731+
},
732+
{
733+
"invalid empty string",
734+
"",
735+
10000,
736+
true,
737+
},
738+
{
739+
"invalid string",
740+
"ew%65",
741+
10000,
742+
true,
743+
},
744+
}
745+
for _, tc := range tests {
746+
t.Run(tc.desc, func(t *testing.T) {
747+
actualInt64, err := ConvertMiBStringToInt64(tc.inputStr)
748+
if err != nil && !tc.expectError {
749+
t.Errorf("Got error %v converting string to int64 %s; expect no error", err, tc.inputStr)
750+
}
751+
if err == nil && tc.expectError {
752+
t.Errorf("Got no error converting string to int64 %s; expect an error", tc.inputStr)
753+
}
754+
if err == nil && actualInt64 != tc.expInt64 {
755+
t.Errorf("Got %d for converting string to int64; expect %d", actualInt64, tc.expInt64)
756+
}
757+
})
758+
}
759+
}

Diff for: pkg/gce-cloud-provider/compute/gce-compute.go

+22-5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ const (
4040
cryptoKeyVerDelimiter = "/cryptoKeyVersions"
4141
)
4242

43+
var hyperdiskTypes = []string{"hyperdisk-extreme", "hyperdisk-throughput"}
44+
4345
type GCEAPIVersion string
4446

4547
const (
@@ -387,15 +389,15 @@ func convertV1CustomerEncryptionKeyToBeta(v1Key *computev1.CustomerEncryptionKey
387389
}
388390
}
389391

390-
func convertV1DiskToBetaDisk(v1Disk *computev1.Disk) *computebeta.Disk {
392+
func convertV1DiskToBetaDisk(v1Disk *computev1.Disk, provisionedThroughputOnCreate int64) *computebeta.Disk {
391393
var dek *computebeta.CustomerEncryptionKey = nil
392394

393395
if v1Disk.DiskEncryptionKey != nil {
394396
dek = convertV1CustomerEncryptionKeyToBeta(v1Disk.DiskEncryptionKey)
395397
}
396398

397399
// Note: this is an incomplete list. It only includes the fields we use for disk creation.
398-
return &computebeta.Disk{
400+
betaDisk := &computebeta.Disk{
399401
Name: v1Disk.Name,
400402
SizeGb: v1Disk.SizeGb,
401403
Description: v1Disk.Description,
@@ -404,6 +406,11 @@ func convertV1DiskToBetaDisk(v1Disk *computev1.Disk) *computebeta.Disk {
404406
ReplicaZones: v1Disk.ReplicaZones,
405407
DiskEncryptionKey: dek,
406408
}
409+
if provisionedThroughputOnCreate > 0 {
410+
betaDisk.ProvisionedThroughput = provisionedThroughputOnCreate
411+
}
412+
413+
return betaDisk
407414
}
408415

409416
func (cloud *CloudProvider) insertRegionalDisk(
@@ -465,7 +472,7 @@ func (cloud *CloudProvider) insertRegionalDisk(
465472

466473
if gceAPIVersion == GCEAPIVersionBeta {
467474
var insertOp *computebeta.Operation
468-
betaDiskToCreate := convertV1DiskToBetaDisk(diskToCreate)
475+
betaDiskToCreate := convertV1DiskToBetaDisk(diskToCreate, 0)
469476
betaDiskToCreate.MultiWriter = multiWriter
470477
insertOp, err = cloud.betaService.RegionDisks.Insert(project, volKey.Region, betaDiskToCreate).Context(ctx).Do()
471478
if insertOp != nil {
@@ -537,7 +544,7 @@ func (cloud *CloudProvider) insertZonalDisk(
537544
gceAPIVersion = GCEAPIVersionV1
538545
)
539546

540-
if multiWriter {
547+
if multiWriter || containsBetaDiskType(hyperdiskTypes, params.DiskType) {
541548
gceAPIVersion = GCEAPIVersionBeta
542549
}
543550

@@ -575,7 +582,7 @@ func (cloud *CloudProvider) insertZonalDisk(
575582

576583
if gceAPIVersion == GCEAPIVersionBeta {
577584
var insertOp *computebeta.Operation
578-
betaDiskToCreate := convertV1DiskToBetaDisk(diskToCreate)
585+
betaDiskToCreate := convertV1DiskToBetaDisk(diskToCreate, params.ProvisionedThroughputOnCreate)
579586
betaDiskToCreate.MultiWriter = multiWriter
580587
insertOp, err = cloud.betaService.Disks.Insert(project, volKey.Zone, betaDiskToCreate).Context(ctx).Do()
581588
if insertOp != nil {
@@ -1167,3 +1174,13 @@ func encodeTags(tags map[string]string) (string, error) {
11671174
}
11681175
return string(enc), nil
11691176
}
1177+
1178+
func containsBetaDiskType(betaDiskTypes []string, diskType string) bool {
1179+
for _, betaDiskType := range betaDiskTypes {
1180+
if betaDiskType == diskType {
1181+
return true
1182+
}
1183+
}
1184+
1185+
return false
1186+
}

Diff for: pkg/gce-pd-csi-driver/controller_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,31 @@ func TestCreateVolumeArguments(t *testing.T) {
817817
},
818818
expErrCode: codes.InvalidArgument,
819819
},
820+
{
821+
name: "success with provisionedThroughput parameter",
822+
req: &csi.CreateVolumeRequest{
823+
Name: name,
824+
CapacityRange: stdCapRange,
825+
VolumeCapabilities: stdVolCaps,
826+
Parameters: map[string]string{"labels": "key1=value1,key2=value2", "provisioned-throughput-on-create": "10000"},
827+
},
828+
expVol: &csi.Volume{
829+
CapacityBytes: common.GbToBytes(20),
830+
VolumeId: testVolumeID,
831+
VolumeContext: nil,
832+
AccessibleTopology: stdTopology,
833+
},
834+
},
835+
{
836+
name: "fail with malformed provisionedThroughput parameter",
837+
req: &csi.CreateVolumeRequest{
838+
Name: name,
839+
CapacityRange: stdCapRange,
840+
VolumeCapabilities: stdVolCaps,
841+
Parameters: map[string]string{"labels": "key1=value1,key2=value2", "provisioned-throughput-on-create": "dsfo3"},
842+
},
843+
expErrCode: codes.InvalidArgument,
844+
},
820845
}
821846

822847
// Run test cases

Diff for: test/e2e/tests/setup_e2e_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
. "github.com/onsi/ginkgo"
2828
. "github.com/onsi/gomega"
2929
computealpha "google.golang.org/api/compute/v0.alpha"
30+
computebeta "google.golang.org/api/compute/v0.beta"
3031
compute "google.golang.org/api/compute/v1"
3132
"k8s.io/klog/v2"
3233
testutils "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/test/e2e/utils"
@@ -46,6 +47,7 @@ var (
4647
testContexts = []*remote.TestContext{}
4748
computeService *compute.Service
4849
computeAlphaService *computealpha.Service
50+
computeBetaService *computebeta.Service
4951
kmsClient *cloudkms.KeyManagementClient
5052
)
5153

@@ -75,6 +77,9 @@ var _ = BeforeSuite(func() {
7577
computeAlphaService, err = remote.GetComputeAlphaClient()
7678
Expect(err).To(BeNil())
7779

80+
computeBetaService, err = remote.GetComputeBetaClient()
81+
Expect(err).To(BeNil())
82+
7883
// Create the KMS client.
7984
kmsClient, err = cloudkms.NewKeyManagementClient(context.Background())
8085
Expect(err).To(BeNil())

Diff for: test/remote/instance.go

+36-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
"golang.org/x/oauth2/google"
3030
computealpha "google.golang.org/api/compute/v0.alpha"
31+
computebeta "google.golang.org/api/compute/v0.beta"
3132
compute "google.golang.org/api/compute/v1"
3233
"google.golang.org/api/googleapi"
3334
"k8s.io/apimachinery/pkg/util/uuid"
@@ -71,12 +72,11 @@ func (i *InstanceInfo) GetNodeID() string {
7172

7273
func CreateInstanceInfo(project, instanceArchitecture, instanceZone, name, machineType string, cs *compute.Service) (*InstanceInfo, error) {
7374
return &InstanceInfo{
74-
project: project,
75-
architecture: instanceArchitecture,
76-
zone: instanceZone,
77-
name: name,
78-
machineType: machineType,
79-
75+
project: project,
76+
architecture: instanceArchitecture,
77+
zone: instanceZone,
78+
name: name,
79+
machineType: machineType,
8080
computeService: cs,
8181
}, nil
8282
}
@@ -330,6 +330,36 @@ func GetComputeAlphaClient() (*computealpha.Service, error) {
330330
return nil, err
331331
}
332332

333+
func GetComputeBetaClient() (*computebeta.Service, error) {
334+
const retries = 10
335+
const backoff = time.Second * 6
336+
337+
klog.V(4).Infof("Getting compute client...")
338+
339+
// Setup the gce client for provisioning instances
340+
// Getting credentials on gce jenkins is flaky, so try a couple times
341+
var err error
342+
var cs *computebeta.Service
343+
for i := 0; i < retries; i++ {
344+
if i > 0 {
345+
time.Sleep(backoff)
346+
}
347+
348+
var client *http.Client
349+
client, err = google.DefaultClient(context.Background(), computebeta.ComputeScope)
350+
if err != nil {
351+
continue
352+
}
353+
354+
cs, err = computebeta.New(client)
355+
if err != nil {
356+
continue
357+
}
358+
return cs, nil
359+
}
360+
return nil, err
361+
}
362+
333363
func generateMetadataWithPublicKey(pubKeyFile string) (*compute.Metadata, error) {
334364
publicKeyByte, err := ioutil.ReadFile(pubKeyFile)
335365
if err != nil {

0 commit comments

Comments
 (0)