Skip to content

Commit a38158b

Browse files
authored
Merge pull request #467 from davidz627/fix/vvc
Implement ValidateVolumeCapabilities and refactor parameter handling for more comprehensive validation of existing disks in all cloud calls
2 parents 7552602 + 57dd986 commit a38158b

File tree

7 files changed

+294
-150
lines changed

7 files changed

+294
-150
lines changed

pkg/common/constants.go

-5
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ limitations under the License.
1717
package common
1818

1919
const (
20-
// Keys for Storage Class Parameters
21-
ParameterKeyType = "type"
22-
ParameterKeyReplicationType = "replication-type"
23-
ParameterKeyDiskEncryptionKmsKey = "disk-encryption-kms-key"
24-
2520
// Keys for Topology. This key will be shared amongst drivers from GCP
2621
TopologyKeyZone = "topology.gke.io/zone"
2722

pkg/common/parameters.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package common
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
)
23+
24+
const (
25+
ParameterKeyType = "type"
26+
ParameterKeyReplicationType = "replication-type"
27+
ParameterKeyDiskEncryptionKmsKey = "disk-encryption-kms-key"
28+
29+
replicationTypeNone = "none"
30+
)
31+
32+
// DiskParameters contains normalized and defaulted disk parameters
33+
type DiskParameters struct {
34+
// Values: pd-standard OR pd-ssd
35+
// Default: pd-standard
36+
DiskType string
37+
// Values: "none", regional-pd
38+
// Default: "none"
39+
ReplicationType string
40+
// Values: {string}
41+
// Default: ""
42+
DiskEncryptionKMSKey string
43+
}
44+
45+
// ExtractAndDefaultParameters will take the relevant parameters from a map and
46+
// put them into a well defined struct making sure to default unspecified fields
47+
func ExtractAndDefaultParameters(parameters map[string]string) (DiskParameters, error) {
48+
p := DiskParameters{
49+
DiskType: "pd-standard", // Default
50+
ReplicationType: replicationTypeNone, // Default
51+
DiskEncryptionKMSKey: "", // Default
52+
}
53+
for k, v := range parameters {
54+
if k == "csiProvisionerSecretName" || k == "csiProvisionerSecretNamespace" {
55+
// These are hardcoded secrets keys required to function but not needed by GCE PD
56+
continue
57+
}
58+
switch strings.ToLower(k) {
59+
case ParameterKeyType:
60+
if v != "" {
61+
p.DiskType = strings.ToLower(v)
62+
}
63+
case ParameterKeyReplicationType:
64+
if v != "" {
65+
p.ReplicationType = strings.ToLower(v)
66+
}
67+
case ParameterKeyDiskEncryptionKmsKey:
68+
// Resource names (e.g. "keyRings", "cryptoKeys", etc.) are case sensitive, so do not change case
69+
p.DiskEncryptionKMSKey = v
70+
default:
71+
return p, fmt.Errorf("parameters contains invalid option %q", k)
72+
}
73+
}
74+
return p, nil
75+
}

pkg/common/parameters_test.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package common
18+
19+
import (
20+
"reflect"
21+
"testing"
22+
)
23+
24+
func TestExtractAndDefaultParameters(t *testing.T) {
25+
tests := []struct {
26+
name string
27+
parameters map[string]string
28+
expectParams DiskParameters
29+
expectErr bool
30+
}{
31+
{
32+
name: "defaults",
33+
parameters: map[string]string{},
34+
expectParams: DiskParameters{
35+
DiskType: "pd-standard",
36+
ReplicationType: "none",
37+
DiskEncryptionKMSKey: "",
38+
},
39+
},
40+
{
41+
name: "specified empties",
42+
parameters: map[string]string{ParameterKeyType: "", ParameterKeyReplicationType: "", ParameterKeyDiskEncryptionKmsKey: ""},
43+
expectParams: DiskParameters{
44+
DiskType: "pd-standard",
45+
ReplicationType: "none",
46+
DiskEncryptionKMSKey: "",
47+
},
48+
},
49+
{
50+
name: "random keys",
51+
parameters: map[string]string{ParameterKeyType: "", "foo": "", ParameterKeyDiskEncryptionKmsKey: ""},
52+
expectErr: true,
53+
},
54+
{
55+
name: "real values",
56+
parameters: map[string]string{ParameterKeyType: "pd-ssd", ParameterKeyReplicationType: "regional-pd", ParameterKeyDiskEncryptionKmsKey: "foo/key"},
57+
expectParams: DiskParameters{
58+
DiskType: "pd-ssd",
59+
ReplicationType: "regional-pd",
60+
DiskEncryptionKMSKey: "foo/key",
61+
},
62+
},
63+
{
64+
name: "partial spec",
65+
parameters: map[string]string{ParameterKeyDiskEncryptionKmsKey: "foo/key"},
66+
expectParams: DiskParameters{
67+
DiskType: "pd-standard",
68+
ReplicationType: "none",
69+
DiskEncryptionKMSKey: "foo/key",
70+
},
71+
},
72+
}
73+
74+
for _, tc := range tests {
75+
t.Run(tc.name, func(t *testing.T) {
76+
p, err := ExtractAndDefaultParameters(tc.parameters)
77+
if gotErr := err != nil; gotErr != tc.expectErr {
78+
t.Fatalf("ExtractAndDefaultParameters(%+v) = %v; expectedErr: %v", tc.parameters, err, tc.expectErr)
79+
}
80+
if err != nil {
81+
return
82+
}
83+
84+
if !reflect.DeepEqual(p, tc.expectParams) {
85+
t.Errorf("ExtractAndDefaultParameters(%+v) = %v; expected params: %v", tc.parameters, p, tc.expectParams)
86+
}
87+
})
88+
}
89+
}

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

+27-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ limitations under the License.
1515
package gcecloudprovider
1616

1717
import (
18+
"strings"
19+
1820
computev1 "google.golang.org/api/compute/v1"
1921
)
2022

@@ -90,15 +92,21 @@ func (d *CloudDisk) GetKind() string {
9092
}
9193
}
9294

93-
func (d *CloudDisk) GetType() string {
95+
// GetPDType returns the type of the PD as either 'pd-standard' or 'pd-ssd' The
96+
// "Type" field on the compute disk is stored as a url like
97+
// projects/project/zones/zone/diskTypes/pd-standard
98+
func (d *CloudDisk) GetPDType() string {
99+
var pdType string
94100
switch d.Type() {
95101
case Zonal:
96-
return d.ZonalDisk.Type
102+
pdType = d.ZonalDisk.Type
97103
case Regional:
98-
return d.RegionalDisk.Type
104+
pdType = d.RegionalDisk.Type
99105
default:
100106
return ""
101107
}
108+
respType := strings.Split(pdType, "/")
109+
return strings.TrimSpace(respType[len(respType)-1])
102110
}
103111

104112
func (d *CloudDisk) GetSelfLink() string {
@@ -155,3 +163,19 @@ func (d *CloudDisk) GetSnapshotId() string {
155163
return ""
156164
}
157165
}
166+
167+
func (d *CloudDisk) GetKMSKeyName() string {
168+
var dek *computev1.CustomerEncryptionKey
169+
switch d.Type() {
170+
case Zonal:
171+
dek = d.ZonalDisk.DiskEncryptionKey
172+
case Regional:
173+
dek = d.RegionalDisk.DiskEncryptionKey
174+
default:
175+
return ""
176+
}
177+
if dek == nil {
178+
return ""
179+
}
180+
return dek.KmsKeyName
181+
}

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

+10-18
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func (cloud *FakeCloudProvider) GetDisk(ctx context.Context, volKey *meta.Key) (
215215
return disk, nil
216216
}
217217

218-
func (cloud *FakeCloudProvider) ValidateExistingDisk(ctx context.Context, resp *CloudDisk, diskType string, reqBytes, limBytes int64) error {
218+
func (cloud *FakeCloudProvider) ValidateExistingDisk(ctx context.Context, resp *CloudDisk, params common.DiskParameters, reqBytes, limBytes int64) error {
219219
if resp == nil {
220220
return fmt.Errorf("disk does not exist")
221221
}
@@ -227,20 +227,12 @@ func (cloud *FakeCloudProvider) ValidateExistingDisk(ctx context.Context, resp *
227227
reqBytes, common.GbToBytes(resp.GetSizeGb()), limBytes)
228228
}
229229

230-
respType := strings.Split(resp.GetType(), "/")
231-
typeMatch := strings.TrimSpace(respType[len(respType)-1]) == strings.TrimSpace(diskType)
232-
typeDefault := diskType == "" && strings.TrimSpace(respType[len(respType)-1]) == "pd-standard"
233-
if !typeMatch && !typeDefault {
234-
return fmt.Errorf("disk already exists with incompatible type. Need %v. Got %v",
235-
diskType, respType[len(respType)-1])
236-
}
237-
klog.V(4).Infof("Compatible disk already exists")
238-
return nil
230+
return ValidateDiskParameters(resp, params)
239231
}
240232

241-
func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, volKey *meta.Key, diskType string, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID, diskEncryptionKmsKey string) error {
233+
func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, volKey *meta.Key, params common.DiskParameters, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID string) error {
242234
if disk, ok := cloud.disks[volKey.Name]; ok {
243-
err := cloud.ValidateExistingDisk(ctx, disk, diskType,
235+
err := cloud.ValidateExistingDisk(ctx, disk, params,
244236
int64(capacityRange.GetRequiredBytes()),
245237
int64(capacityRange.GetLimitBytes()))
246238
if err != nil {
@@ -255,13 +247,13 @@ func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, volKey *meta.Key
255247
Name: volKey.Name,
256248
SizeGb: common.BytesToGb(capBytes),
257249
Description: "Disk created by GCE-PD CSI Driver",
258-
Type: cloud.GetDiskTypeURI(volKey, diskType),
250+
Type: cloud.GetDiskTypeURI(volKey, params.DiskType),
259251
SelfLink: fmt.Sprintf("projects/%s/zones/%s/disks/%s", cloud.project, volKey.Zone, volKey.Name),
260252
SourceSnapshotId: snapshotID,
261253
}
262-
if diskEncryptionKmsKey != "" {
254+
if params.DiskEncryptionKMSKey != "" {
263255
diskToCreateGA.DiskEncryptionKey = &computev1.CustomerEncryptionKey{
264-
KmsKeyName: diskEncryptionKmsKey,
256+
KmsKeyName: params.DiskEncryptionKMSKey,
265257
}
266258
}
267259
diskToCreate = ZonalCloudDisk(diskToCreateGA)
@@ -270,13 +262,13 @@ func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, volKey *meta.Key
270262
Name: volKey.Name,
271263
SizeGb: common.BytesToGb(capBytes),
272264
Description: "Regional disk created by GCE-PD CSI Driver",
273-
Type: cloud.GetDiskTypeURI(volKey, diskType),
265+
Type: cloud.GetDiskTypeURI(volKey, params.DiskType),
274266
SelfLink: fmt.Sprintf("projects/%s/regions/%s/disks/%s", cloud.project, volKey.Region, volKey.Name),
275267
SourceSnapshotId: snapshotID,
276268
}
277-
if diskEncryptionKmsKey != "" {
269+
if params.DiskEncryptionKMSKey != "" {
278270
diskToCreateV1.DiskEncryptionKey = &computev1.CustomerEncryptionKey{
279-
KmsKeyName: diskEncryptionKmsKey,
271+
KmsKeyName: params.DiskEncryptionKMSKey,
280272
}
281273
}
282274
diskToCreate = RegionalCloudDisk(diskToCreateV1)

0 commit comments

Comments
 (0)