Skip to content

Commit 63146c9

Browse files
authored
Merge pull request #1526 from amacaskill/createVolumeStoragePools
Validate storage pools in CreateVolume
2 parents 2c674dd + fc7a6b5 commit 63146c9

File tree

9 files changed

+939
-16
lines changed

9 files changed

+939
-16
lines changed

pkg/common/parameters.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ type DiskParameters struct {
9797
ForceAttach bool
9898
// Values: {[]string}
9999
// Default: ""
100-
StoragePools []string
100+
StoragePools []StoragePool
101101
}
102102

103103
// SnapshotParameters contains normalized and defaulted parameters for snapshots
@@ -109,6 +109,13 @@ type SnapshotParameters struct {
109109
Labels map[string]string
110110
}
111111

112+
type StoragePool struct {
113+
Project string
114+
Zone string
115+
Name string
116+
ResourceName string
117+
}
118+
112119
// ExtractAndDefaultParameters will take the relevant parameters from a map and
113120
// put them into a well defined struct making sure to default unspecified fields.
114121
// extraVolumeLabels are added as labels; if there are also labels specified in

pkg/common/parameters_test.go

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package common
1919
import (
2020
"reflect"
2121
"testing"
22+
23+
"github.com/google/go-cmp/cmp"
2224
)
2325

2426
func TestExtractAndDefaultParameters(t *testing.T) {
@@ -199,6 +201,67 @@ func TestExtractAndDefaultParameters(t *testing.T) {
199201
Labels: map[string]string{},
200202
},
201203
},
204+
{
205+
name: "storage pool parameters",
206+
parameters: map[string]string{ParameterKeyType: "hyperdisk-balanced", ParameterKeyStoragePools: "projects/my-project/zones/us-central1-a/storagePools/storagePool-1,projects/my-project/zones/us-central1-b/storagePools/storagePool-2"},
207+
labels: map[string]string{},
208+
expectParams: DiskParameters{
209+
DiskType: "hyperdisk-balanced",
210+
ReplicationType: "none",
211+
Tags: map[string]string{},
212+
Labels: map[string]string{},
213+
StoragePools: []StoragePool{
214+
{
215+
Project: "my-project",
216+
Zone: "us-central1-a",
217+
Name: "storagePool-1",
218+
ResourceName: "projects/my-project/zones/us-central1-a/storagePools/storagePool-1",
219+
},
220+
{
221+
Project: "my-project",
222+
Zone: "us-central1-b",
223+
Name: "storagePool-2",
224+
ResourceName: "projects/my-project/zones/us-central1-b/storagePools/storagePool-2",
225+
},
226+
},
227+
},
228+
},
229+
{
230+
name: "invalid storage pool parameters, starts with /projects instead of projects",
231+
parameters: map[string]string{ParameterKeyType: "hyperdisk-balanced", ParameterKeyStoragePools: "/projects/my-project/zones/us-central1-a/storagePools/storagePool-1"},
232+
labels: map[string]string{},
233+
expectErr: true,
234+
},
235+
{
236+
name: "invalid storage pool parameters, missing projects",
237+
parameters: map[string]string{ParameterKeyType: "hyperdisk-balanced", ParameterKeyStoragePools: "zones/us-central1-a/storagePools/storagePool-1"},
238+
labels: map[string]string{},
239+
expectErr: true,
240+
},
241+
{
242+
name: "invalid storage pool parameters, missing zones",
243+
parameters: map[string]string{ParameterKeyType: "hyperdisk-balanced", ParameterKeyStoragePools: "projects/my-project/storagePools/storagePool-1"},
244+
labels: map[string]string{},
245+
expectErr: true,
246+
},
247+
{
248+
name: "invalid storage pool parameters, duplicate projects",
249+
parameters: map[string]string{ParameterKeyType: "hyperdisk-balanced", ParameterKeyStoragePools: "projects/my-project/projects/my-project/storagePools/storagePool-1"},
250+
labels: map[string]string{},
251+
expectErr: true,
252+
},
253+
{
254+
name: "invalid storage pool parameters, duplicate zones",
255+
parameters: map[string]string{ParameterKeyType: "hyperdisk-balanced", ParameterKeyStoragePools: "zones/us-central1-a/zones/us-central1-a/storagePools/storagePool-1"},
256+
labels: map[string]string{},
257+
expectErr: true,
258+
},
259+
{
260+
name: "invalid storage pool parameters, duplicate storagePools",
261+
parameters: map[string]string{ParameterKeyType: "hyperdisk-balanced", ParameterKeyStoragePools: "projects/my-project/storagePools/us-central1-a/storagePools/storagePool-1"},
262+
labels: map[string]string{},
263+
expectErr: true,
264+
},
202265
}
203266

204267
for _, tc := range tests {
@@ -211,8 +274,8 @@ func TestExtractAndDefaultParameters(t *testing.T) {
211274
return
212275
}
213276

214-
if !reflect.DeepEqual(p, tc.expectParams) {
215-
t.Errorf("ExtractAndDefaultParameters(%+v) = %v; expected params: %v", tc.parameters, p, tc.expectParams)
277+
if diff := cmp.Diff(p, tc.expectParams); diff != "" {
278+
t.Errorf("ExtractAndDefaultParameters(%+v): -want, +got \n%s", tc.parameters, diff)
216279
}
217280
})
218281
}

pkg/common/utils.go

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ var (
8080
// zones/zone/machineTypes/machine-type
8181
machineTypeRegex = regexp.MustCompile(machineTypePattern)
8282

83+
storagePoolFieldsRegex = regexp.MustCompile(`^projects/([^/]+)/zones/([^/]+)/storagePools/([^/]+)$`)
84+
8385
// userErrorCodeMap tells how API error types are translated to error codes.
8486
userErrorCodeMap = map[int]codes.Code{
8587
http.StatusForbidden: codes.PermissionDenied,
@@ -393,15 +395,72 @@ func isValidDiskEncryptionKmsKey(DiskEncryptionKmsKey string) bool {
393395
return kmsKeyPattern.MatchString(DiskEncryptionKmsKey)
394396
}
395397

396-
// TODO(amacaskill): Implement this function.
397398
// ParseStoragePools returns an error if none of the given storagePools
398399
// (delimited by a comma) are in the format
399400
// projects/project/zones/zone/storagePools/storagePool.
400-
func ParseStoragePools(storagePools string) ([]string, error) {
401-
return nil, status.Errorf(codes.Unimplemented, "")
401+
func ParseStoragePools(storagePools string) ([]StoragePool, error) {
402+
spSlice := strings.Split(storagePools, ",")
403+
parsedStoragePools := []StoragePool{}
404+
for _, sp := range spSlice {
405+
project, location, spName, err := fieldsFromStoragePoolResourceName(sp)
406+
if err != nil {
407+
return nil, err
408+
}
409+
spObj := StoragePool{Project: project, Zone: location, Name: spName, ResourceName: sp}
410+
parsedStoragePools = append(parsedStoragePools, spObj)
411+
412+
}
413+
return parsedStoragePools, nil
402414
}
403415

404-
// TODO(amacaskill): Implement this function.
405-
func StoragePoolInZone(storagePools []string, zone string) string {
406-
return ""
416+
// fieldsFromResourceName returns the project, zone, and Storage Pool name from the given
417+
// Storage Pool resource name. The resource name must be in the format
418+
// projects/project/zones/zone/storagePools/storagePool.
419+
// All other formats are invalid, and an error will be returned.
420+
func fieldsFromStoragePoolResourceName(resourceName string) (project, location, spName string, err error) {
421+
fieldMatches := storagePoolFieldsRegex.FindStringSubmatch(resourceName)
422+
// Field matches should have 4 strings: [resourceName, project, zone, storagePool]. The first
423+
// match is the entire string.
424+
if len(fieldMatches) != 4 {
425+
err := fmt.Errorf("invalid Storage Pool resource name. Got %s, expected projects/project/zones/zone/storagePools/storagePool", resourceName)
426+
return "", "", "", err
427+
}
428+
project = fieldMatches[1]
429+
location = fieldMatches[2]
430+
spName = fieldMatches[3]
431+
return
432+
}
433+
434+
// StoragePoolZones returns the unique zones of the given storage pool resource names.
435+
// Returns an error if multiple storage pools in 1 zone are found.
436+
func StoragePoolZones(storagePools []StoragePool) ([]string, error) {
437+
zonesSet := sets.String{}
438+
var zones []string
439+
for _, sp := range storagePools {
440+
if zonesSet.Has(sp.Zone) {
441+
return nil, fmt.Errorf("found multiple storage pools in zone %s. Only one storage pool per zone is allowed", sp.Zone)
442+
}
443+
zonesSet.Insert(sp.Zone)
444+
zones = append(zones, sp.Zone)
445+
}
446+
return zones, nil
447+
}
448+
449+
func StoragePoolInZone(storagePools []StoragePool, zone string) *StoragePool {
450+
for _, pool := range storagePools {
451+
if zone == pool.Zone {
452+
return &pool
453+
}
454+
}
455+
return nil
456+
}
457+
458+
func UnorderedSlicesEqual(slice1 []string, slice2 []string) bool {
459+
set1 := sets.NewString(slice1...)
460+
set2 := sets.NewString(slice2...)
461+
spZonesNotInReq := set1.Difference(set2)
462+
if spZonesNotInReq.Len() != 0 {
463+
return false
464+
}
465+
return true
407466
}

0 commit comments

Comments
 (0)