Skip to content

Automated cherry pick of #1079: Add provisionedIops for pd-extreme #1101: Add provisionedThroughput for hyperdisk #1241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ See Github [Issues](https://github.com/kubernetes-sigs/gcp-compute-persistent-di

### CreateVolume Parameters

| Parameter | Values | Default | Description |
|------------------|---------------------------|---------------|----------------------------------------------------------------------------------------------------|
| 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 |
| replication-type | `none` OR `regional-pd` | `none` | Replication type allows you to choose between Zonal Persistent Disks or Regional Persistent Disks |
| 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. |
| labels | `key1=value1,key2=value2` | | Labels allow you to assign custom [GCE Disk labels](https://cloud.google.com/compute/docs/labeling-resources). |
| Parameter | Values | Default | Description |
|-----------------------------|---------------------------|---------------|----------------------------------------------------------------------------------------------------|
| 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 |
| replication-type | `none` OR `regional-pd` | `none` | Replication type allows you to choose between Zonal Persistent Disks or Regional Persistent Disks |
| 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. |
| labels | `key1=value1,key2=value2` | | Labels allow you to assign custom [GCE Disk labels](https://cloud.google.com/compute/docs/labeling-resources). |
| 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. |
| 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. |

### Topology

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require (
gopkg.in/gcfg.v1 v1.2.3
k8s.io/apimachinery v0.24.1
k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
k8s.io/cloud-provider v0.24.1
k8s.io/component-base v0.24.1
k8s.io/klog/v2 v2.60.1
k8s.io/kubernetes v1.24.1
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2432,6 +2432,7 @@ k8s.io/apiserver v0.24.1/go.mod h1:dQWNMx15S8NqJMp0gpYfssyvhYnkilc1LpExd/dkLh0=
k8s.io/cli-runtime v0.24.1/go.mod h1:14aVvCTqkA7dNXY51N/6hRY3GUjchyWDOwW84qmR3bs=
k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E=
k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8=
k8s.io/cloud-provider v0.24.1 h1:SaQNq2Ax+epdY9wFngwN9GWpOVnM72hUqr2qy20cOvg=
k8s.io/cloud-provider v0.24.1/go.mod h1:h5m/KIiwiQ76hpUBsgrwm/rxteIfJG9kJQ/+/w1as2M=
k8s.io/cluster-bootstrap v0.24.1/go.mod h1:uq2PiYfKh8ZLb6DBU/3/2Z1DkMqXkTOHLemalC4tOgE=
k8s.io/code-generator v0.24.1/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w=
Expand Down
28 changes: 24 additions & 4 deletions pkg/common/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import (

const (
// Parameters for StorageClass
ParameterKeyType = "type"
ParameterKeyReplicationType = "replication-type"
ParameterKeyDiskEncryptionKmsKey = "disk-encryption-kms-key"
ParameterKeyLabels = "labels"
ParameterKeyType = "type"
ParameterKeyReplicationType = "replication-type"
ParameterKeyDiskEncryptionKmsKey = "disk-encryption-kms-key"
ParameterKeyLabels = "labels"
ParameterKeyProvisionedIOPSOnCreate = "provisioned-iops-on-create"
ParameterKeyProvisionedThroughputOnCreate = "provisioned-throughput-on-create"

// Parameters for VolumeSnapshotClass
ParameterKeyStorageLocations = "storage-locations"
Expand Down Expand Up @@ -75,6 +77,12 @@ type DiskParameters struct {
// Values: {map[string]string}
// Default: ""
Labels map[string]string
// Values: {int64}
// Default: none
ProvisionedIOPSOnCreate int64
// Values: {int64}
// Default: none
ProvisionedThroughputOnCreate int64
}

// SnapshotParameters contains normalized and defaulted parameters for snapshots
Expand Down Expand Up @@ -135,6 +143,18 @@ func ExtractAndDefaultParameters(parameters map[string]string, driverName string
for labelKey, labelValue := range paramLabels {
p.Labels[labelKey] = labelValue
}
case ParameterKeyProvisionedIOPSOnCreate:
paramProvisionedIOPSOnCreate, err := ConvertStringToInt64(v)
if err != nil {
return p, fmt.Errorf("parameters contain invalid provisionedIOPSOnCreate parameter: %w", err)
}
p.ProvisionedIOPSOnCreate = paramProvisionedIOPSOnCreate
case ParameterKeyProvisionedThroughputOnCreate:
paramProvisionedThroughputOnCreate, err := ConvertMiStringToInt64(v)
if err != nil {
return p, fmt.Errorf("parameters contain invalid provisionedThroughputOnCreate parameter: %w", err)
}
p.ProvisionedThroughputOnCreate = paramProvisionedThroughputOnCreate
default:
return p, fmt.Errorf("parameters contains invalid option %q", k)
}
Expand Down
32 changes: 32 additions & 0 deletions pkg/common/parameters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,38 @@ func TestExtractAndDefaultParameters(t *testing.T) {
},
},
},
{
name: "values from parameters, checking pd-extreme",
parameters: map[string]string{ParameterKeyType: "pd-extreme", ParameterKeyReplicationType: "none", ParameterKeyDiskEncryptionKmsKey: "foo/key", ParameterKeyLabels: "key1=value1,key2=value2", ParameterKeyProvisionedIOPSOnCreate: "10k"},
labels: map[string]string{},
expectParams: DiskParameters{
DiskType: "pd-extreme",
ReplicationType: "none",
DiskEncryptionKMSKey: "foo/key",
Tags: map[string]string{},
Labels: map[string]string{
"key1": "value1",
"key2": "value2",
},
ProvisionedIOPSOnCreate: 10000,
},
},
{
name: "values from parameters, checking hyperdisk-throughput",
parameters: map[string]string{ParameterKeyType: "hyperdisk-throughput", ParameterKeyReplicationType: "none", ParameterKeyDiskEncryptionKmsKey: "foo/key", ParameterKeyLabels: "key1=value1,key2=value2", ParameterKeyProvisionedThroughputOnCreate: "1000Mi"},
labels: map[string]string{},
expectParams: DiskParameters{
DiskType: "hyperdisk-throughput",
ReplicationType: "none",
DiskEncryptionKMSKey: "foo/key",
Tags: map[string]string{},
Labels: map[string]string{
"key1": "value1",
"key2": "value2",
},
ProvisionedThroughputOnCreate: 1000,
},
},
{
name: "values from parameters, checking balanced pd",
parameters: map[string]string{ParameterKeyType: "pd-balanced", ParameterKeyReplicationType: "regional-pd", ParameterKeyDiskEncryptionKmsKey: "foo/key"},
Expand Down
20 changes: 20 additions & 0 deletions pkg/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import (
"strings"

"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/sets"
volumehelpers "k8s.io/cloud-provider/volume/helpers"
)

const (
Expand Down Expand Up @@ -268,3 +270,21 @@ func ParseMachineType(machineTypeUrl string) (string, error) {
}
return machineType[1], nil
}

// ConvertStringToInt64 converts a string to int64
func ConvertStringToInt64(str string) (int64, error) {
quantity, err := resource.ParseQuantity(str)
if err != nil {
return -1, err
}
return volumehelpers.RoundUpToB(quantity)
}

// ConvertMiStringToInt64 converts a GiB string to int64
func ConvertMiStringToInt64(str string) (int64, error) {
quantity, err := resource.ParseQuantity(str)
if err != nil {
return -1, err
}
return volumehelpers.RoundUpToMiB(quantity)
}
222 changes: 222 additions & 0 deletions pkg/common/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,225 @@ func TestParseMachineType(t *testing.T) {
})
}
}

func TestConvertStringToInt64(t *testing.T) {
tests := []struct {
desc string
inputStr string
expInt64 int64
expectError bool
}{
{
desc: "valid number string",
inputStr: "10000",
expInt64: 10000,
expectError: false,
},
{
desc: "round M to number",
inputStr: "1M",
expInt64: 1000000,
expectError: false,
},
{
desc: "round m to number",
inputStr: "1m",
expInt64: 1,
expectError: false,
},
{
desc: "round k to number",
inputStr: "1k",
expInt64: 1000,
expectError: false,
},
{
desc: "invalid empty string",
inputStr: "",
expInt64: 0,
expectError: true,
},
{
desc: "invalid string",
inputStr: "ew%65",
expInt64: 0,
expectError: true,
},
{
desc: "invalid KiB string",
inputStr: "10KiB",
expInt64: 10000,
expectError: true,
},
{
desc: "invalid GB string",
inputStr: "10GB",
expInt64: 0,
expectError: true,
},
{
desc: "round Ki to number",
inputStr: "1Ki",
expInt64: 1024,
expectError: false,
},
{
desc: "round k to number",
inputStr: "10k",
expInt64: 10000,
expectError: false,
},
{
desc: "round Mi to number",
inputStr: "10Mi",
expInt64: 10485760,
expectError: false,
},
{
desc: "round M to number",
inputStr: "10M",
expInt64: 10000000,
expectError: false,
},
{
desc: "round G to number",
inputStr: "10G",
expInt64: 10000000000,
expectError: false,
},
{
desc: "round Gi to number",
inputStr: "100Gi",
expInt64: 107374182400,
expectError: false,
},
{
desc: "round decimal to number",
inputStr: "1.2Gi",
expInt64: 1288490189,
expectError: false,
},
{
desc: "round big value to number",
inputStr: "8191Pi",
expInt64: 9222246136947933184,
expectError: false,
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
actualInt64, err := ConvertStringToInt64(tc.inputStr)
if err != nil && !tc.expectError {
t.Errorf("Got error %v converting string to int64 %s; expect no error", err, tc.inputStr)
}
if err == nil && tc.expectError {
t.Errorf("Got no error converting string to int64 %s; expect an error", tc.inputStr)
}
if err == nil && actualInt64 != tc.expInt64 {
t.Errorf("Got %d for converting string to int64; expect %d", actualInt64, tc.expInt64)
}
})
}
}

func TestConvertMiStringToInt64(t *testing.T) {
tests := []struct {
desc string
inputStr string
expInt64 int64
expectError bool
}{
{
desc: "valid number string",
inputStr: "10000",
expInt64: 1,
expectError: false,
},
{
desc: "round Ki to MiB",
inputStr: "1000Ki",
expInt64: 1,
expectError: false,
},
{
desc: "round k to MiB",
inputStr: "1000k",
expInt64: 1,
expectError: false,
},
{
desc: "round Mi to MiB",
inputStr: "1000Mi",
expInt64: 1000,
expectError: false,
},
{
desc: "round M to MiB",
inputStr: "1000M",
expInt64: 954,
expectError: false,
},
{
desc: "round G to MiB",
inputStr: "1000G",
expInt64: 953675,
expectError: false,
},
{
desc: "round Gi to MiB",
inputStr: "10000Gi",
expInt64: 10240000,
expectError: false,
},
{
desc: "round decimal to MiB",
inputStr: "1.2Gi",
expInt64: 1229,
expectError: false,
},
{
desc: "round big value to MiB",
inputStr: "8191Pi",
expInt64: 8795019280384,
expectError: false,
},
{
desc: "invalid empty string",
inputStr: "",
expInt64: 0,
expectError: true,
},
{
desc: "invalid KiB string",
inputStr: "10KiB",
expInt64: 10000,
expectError: true,
},
{
desc: "invalid GB string",
inputStr: "10GB",
expInt64: 0,
expectError: true,
},
{
desc: "invalid string",
inputStr: "ew%65",
expInt64: 0,
expectError: true,
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
actualInt64, err := ConvertMiStringToInt64(tc.inputStr)
if err != nil && !tc.expectError {
t.Errorf("Got error %v converting string to int64 %s; expect no error", err, tc.inputStr)
}
if err == nil && tc.expectError {
t.Errorf("Got no error converting string to int64 %s; expect an error", tc.inputStr)
}
if err == nil && actualInt64 != tc.expInt64 {
t.Errorf("Got %d for converting string to int64; expect %d", actualInt64, tc.expInt64)
}
})
}
}
Loading