@@ -34,9 +34,7 @@ import (
34
34
"google.golang.org/api/googleapi"
35
35
"google.golang.org/grpc/codes"
36
36
"google.golang.org/grpc/status"
37
- "k8s.io/apimachinery/pkg/api/resource"
38
37
"k8s.io/apimachinery/pkg/util/sets"
39
- volumehelpers "k8s.io/cloud-provider/volume/helpers"
40
38
"k8s.io/klog/v2"
41
39
"sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/constants"
42
40
)
@@ -88,18 +86,21 @@ const (
88
86
HyperdiskExtremeIopsPerGB = 2
89
87
HyperdiskThroughputThroughputPerGB = 10
90
88
BytesInGB = 1024
89
+
90
+ // Parameters for AvailabilityClass
91
+ ParameterNoAvailabilityClass = "none"
92
+ ParameterRegionalHardFailoverClass = "regional-hard-failover"
93
+
94
+ // Parameters for VolumeSnapshotClass
95
+ DiskSnapshotType = "snapshots"
96
+ DiskImageType = "images"
91
97
)
92
98
93
99
var (
94
- multiRegionalPattern = regexp .MustCompile (multiRegionalLocationFmt )
95
- regionalPattern = regexp .MustCompile (regionalLocationFmt )
96
-
97
100
// Full or partial URL of the machine type resource, in the format:
98
101
// zones/zone/machineTypes/machine-type
99
102
machineTypeRegex = regexp .MustCompile (machineTypePattern )
100
103
101
- storagePoolFieldsRegex = regexp .MustCompile (`^projects/([^/]+)/zones/([^/]+)/storagePools/([^/]+)$` )
102
-
103
104
zoneURIRegex = regexp .MustCompile (zoneURIPattern )
104
105
105
106
// userErrorCodeMap tells how API error types are translated to error codes.
@@ -111,16 +112,16 @@ var (
111
112
http .StatusConflict : codes .FailedPrecondition ,
112
113
}
113
114
114
- validDataCacheMode = []string {constants .DataCacheModeWriteBack , constants .DataCacheModeWriteThrough }
115
-
116
- // Regular expressions for validating parent_id, key and value of a resource tag.
117
- regexParent = regexp .MustCompile (`(^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)` )
118
- regexKey = regexp .MustCompile (`^[a-zA-Z0-9]([0-9A-Za-z_.-]{0,61}[a-zA-Z0-9])?$` )
119
- regexValue = regexp .MustCompile (`^[a-zA-Z0-9]([0-9A-Za-z_.@%=+:,*#&()\[\]{}\-\s]{0,61}[a-zA-Z0-9])?$` )
120
-
121
115
csiRetryableErrorCodes = []codes.Code {codes .Canceled , codes .DeadlineExceeded , codes .Unavailable , codes .Aborted , codes .ResourceExhausted }
122
116
)
123
117
118
+ type StoragePool struct {
119
+ Project string
120
+ Zone string
121
+ Name string
122
+ ResourceName string
123
+ }
124
+
124
125
func BytesToGbRoundDown (bytes int64 ) int64 {
125
126
// TODO: Throw an error when div to 0
126
127
return bytes / (1024 * 1024 * 1024 )
@@ -234,153 +235,6 @@ func CreateZonalVolumeID(project, zone, name string) string {
234
235
return fmt .Sprintf (volIDZonalFmt , project , zone , name )
235
236
}
236
237
237
- // ConvertLabelsStringToMap converts the labels from string to map
238
- // example: "key1=value1,key2=value2" gets converted into {"key1": "value1", "key2": "value2"}
239
- // See https://cloud.google.com/compute/docs/labeling-resources#label_format for details.
240
- func ConvertLabelsStringToMap (labels string ) (map [string ]string , error ) {
241
- const labelsDelimiter = ","
242
- const labelsKeyValueDelimiter = "="
243
-
244
- labelsMap := make (map [string ]string )
245
- if labels == "" {
246
- return labelsMap , nil
247
- }
248
-
249
- regexKey , _ := regexp .Compile (`^\p{Ll}[\p{Ll}0-9_-]{0,62}$` )
250
- checkLabelKeyFn := func (key string ) error {
251
- if ! regexKey .MatchString (key ) {
252
- return fmt .Errorf ("label value %q is invalid (should start with lowercase letter / lowercase letter, digit, _ and - chars are allowed / 1-63 characters" , key )
253
- }
254
- return nil
255
- }
256
-
257
- regexValue , _ := regexp .Compile (`^[\p{Ll}0-9_-]{0,63}$` )
258
- checkLabelValueFn := func (value string ) error {
259
- if ! regexValue .MatchString (value ) {
260
- return fmt .Errorf ("label value %q is invalid (lowercase letter, digit, _ and - chars are allowed / 0-63 characters" , value )
261
- }
262
-
263
- return nil
264
- }
265
-
266
- keyValueStrings := strings .Split (labels , labelsDelimiter )
267
- for _ , keyValue := range keyValueStrings {
268
- keyValue := strings .Split (keyValue , labelsKeyValueDelimiter )
269
-
270
- if len (keyValue ) != 2 {
271
- return nil , fmt .Errorf ("labels %q are invalid, correct format: 'key1=value1,key2=value2'" , labels )
272
- }
273
-
274
- key := strings .TrimSpace (keyValue [0 ])
275
- if err := checkLabelKeyFn (key ); err != nil {
276
- return nil , err
277
- }
278
-
279
- value := strings .TrimSpace (keyValue [1 ])
280
- if err := checkLabelValueFn (value ); err != nil {
281
- return nil , err
282
- }
283
-
284
- labelsMap [key ] = value
285
- }
286
-
287
- const maxNumberOfLabels = 64
288
- if len (labelsMap ) > maxNumberOfLabels {
289
- return nil , fmt .Errorf ("more than %d labels is not allowed, given: %d" , maxNumberOfLabels , len (labelsMap ))
290
- }
291
-
292
- return labelsMap , nil
293
- }
294
-
295
- // ConvertTagsStringToMap converts the tags from string to Tag slice
296
- // example: "parent_id1/tag_key1/tag_value1,parent_id2/tag_key2/tag_value2" gets
297
- // converted into {"parent_id1/tag_key1":"tag_value1", "parent_id2/tag_key2":"tag_value2"}
298
- // See https://cloud.google.com/resource-manager/docs/tags/tags-overview,
299
- // https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing for details
300
- func ConvertTagsStringToMap (tags string ) (map [string ]string , error ) {
301
- const tagsDelimiter = ","
302
- const tagsParentIDKeyValueDelimiter = "/"
303
-
304
- tagsMap := make (map [string ]string )
305
- if tags == "" {
306
- return nil , nil
307
- }
308
-
309
- checkTagParentIDFn := func (tag , parentID string ) error {
310
- if ! regexParent .MatchString (parentID ) {
311
- return fmt .Errorf ("tag parent_id %q for tag %q is invalid. parent_id can have a maximum of 32 characters and cannot be empty. parent_id can be either OrganizationID or ProjectID. OrganizationID must consist of decimal numbers, and cannot have leading zeroes and ProjectID must be 6 to 30 characters in length, can only contain lowercase letters, numbers, and hyphens, and must start with a letter, and cannot end with a hyphen" , parentID , tag )
312
- }
313
- return nil
314
- }
315
-
316
- checkTagKeyFn := func (tag , key string ) error {
317
- if ! regexKey .MatchString (key ) {
318
- return fmt .Errorf ("tag key %q for tag %q is invalid. Tag key can have a maximum of 63 characters and cannot be empty. Tag key must begin and end with an alphanumeric character, and must contain only uppercase, lowercase alphanumeric characters, and the following special characters `._-`" , key , tag )
319
- }
320
- return nil
321
- }
322
-
323
- checkTagValueFn := func (tag , value string ) error {
324
- if ! regexValue .MatchString (value ) {
325
- return fmt .Errorf ("tag value %q for tag %q is invalid. Tag value can have a maximum of 63 characters and cannot be empty. Tag value must begin and end with an alphanumeric character, and must contain only uppercase, lowercase alphanumeric characters, and the following special characters `_-.@%%=+:,*#&(){}[]` and spaces" , value , tag )
326
- }
327
-
328
- return nil
329
- }
330
-
331
- checkTagParentIDKey := sets.String {}
332
- parentIDkeyValueStrings := strings .Split (tags , tagsDelimiter )
333
- for _ , parentIDkeyValueString := range parentIDkeyValueStrings {
334
- parentIDKeyValue := strings .Split (parentIDkeyValueString , tagsParentIDKeyValueDelimiter )
335
-
336
- if len (parentIDKeyValue ) != 3 {
337
- return nil , fmt .Errorf ("tag %q is invalid, correct format: 'parent_id1/key1/value1,parent_id2/key2/value2'" , parentIDkeyValueString )
338
- }
339
-
340
- parentID := strings .TrimSpace (parentIDKeyValue [0 ])
341
- if err := checkTagParentIDFn (parentIDkeyValueString , parentID ); err != nil {
342
- return nil , err
343
- }
344
-
345
- key := strings .TrimSpace (parentIDKeyValue [1 ])
346
- if err := checkTagKeyFn (parentIDkeyValueString , key ); err != nil {
347
- return nil , err
348
- }
349
-
350
- value := strings .TrimSpace (parentIDKeyValue [2 ])
351
- if err := checkTagValueFn (parentIDkeyValueString , value ); err != nil {
352
- return nil , err
353
- }
354
-
355
- parentIDKeyStr := fmt .Sprintf ("%s/%s" , parentID , key )
356
- if checkTagParentIDKey .Has (parentIDKeyStr ) {
357
- return nil , fmt .Errorf ("tag parent_id & key combination %q exists more than once" , parentIDKeyStr )
358
- }
359
- checkTagParentIDKey .Insert (parentIDKeyStr )
360
-
361
- tagsMap [parentIDKeyStr ] = value
362
- }
363
-
364
- // The maximum number of tags allowed per resource is 50. For more details check the following:
365
- // https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing#attaching
366
- // https://cloud.google.com/resource-manager/docs/limits#tag-limits
367
- const maxNumberOfTags = 50
368
- if len (tagsMap ) > maxNumberOfTags {
369
- return nil , fmt .Errorf ("more than %d tags is not allowed, given: %d" , maxNumberOfTags , len (tagsMap ))
370
- }
371
-
372
- return tagsMap , nil
373
- }
374
-
375
- // ProcessStorageLocations trims and normalizes storage location to lower letters.
376
- func ProcessStorageLocations (storageLocations string ) ([]string , error ) {
377
- normalizedLoc := strings .ToLower (strings .TrimSpace (storageLocations ))
378
- if ! multiRegionalPattern .MatchString (normalizedLoc ) && ! regionalPattern .MatchString (normalizedLoc ) {
379
- return []string {}, fmt .Errorf ("invalid location for snapshot: %q" , storageLocations )
380
- }
381
- return []string {normalizedLoc }, nil
382
- }
383
-
384
238
// ValidateSnapshotType validates the type
385
239
func ValidateSnapshotType (snapshotType string ) error {
386
240
switch snapshotType {
@@ -391,55 +245,6 @@ func ValidateSnapshotType(snapshotType string) error {
391
245
}
392
246
}
393
247
394
- // ConvertStringToInt64 converts a string to int64
395
- func ConvertStringToInt64 (str string ) (int64 , error ) {
396
- quantity , err := resource .ParseQuantity (str )
397
- if err != nil {
398
- return - 1 , err
399
- }
400
- return volumehelpers .RoundUpToB (quantity )
401
- }
402
-
403
- // ConvertMiStringToInt64 converts a GiB string to int64
404
- func ConvertMiStringToInt64 (str string ) (int64 , error ) {
405
- quantity , err := resource .ParseQuantity (str )
406
- if err != nil {
407
- return - 1 , err
408
- }
409
- return volumehelpers .RoundUpToMiB (quantity )
410
- }
411
-
412
- // ConvertGiStringToInt64 converts a GiB string to int64
413
- func ConvertGiStringToInt64 (str string ) (int64 , error ) {
414
- quantity , err := resource .ParseQuantity (str )
415
- if err != nil {
416
- return - 1 , err
417
- }
418
- return volumehelpers .RoundUpToGiB (quantity )
419
- }
420
-
421
- // ConvertStringToBool converts a string to a boolean.
422
- func ConvertStringToBool (str string ) (bool , error ) {
423
- switch strings .ToLower (str ) {
424
- case "true" :
425
- return true , nil
426
- case "false" :
427
- return false , nil
428
- }
429
- return false , fmt .Errorf ("Unexpected boolean string %s" , str )
430
- }
431
-
432
- // ConvertStringToAvailabilityClass converts a string to an availability class string.
433
- func ConvertStringToAvailabilityClass (str string ) (string , error ) {
434
- switch strings .ToLower (str ) {
435
- case ParameterNoAvailabilityClass :
436
- return ParameterNoAvailabilityClass , nil
437
- case ParameterRegionalHardFailoverClass :
438
- return ParameterRegionalHardFailoverClass , nil
439
- }
440
- return "" , fmt .Errorf ("Unexpected boolean string %s" , str )
441
- }
442
-
443
248
// ParseMachineType returns an extracted machineType from a URL, or empty if not found.
444
249
// machineTypeUrl: Full or partial URL of the machine type resource, in the format:
445
250
//
@@ -614,12 +419,6 @@ func NewCombinedError(msg string, errs []error) error {
614
419
return LoggedError (msg , errors .Join (errs ... ))
615
420
}
616
421
617
- func isValidDiskEncryptionKmsKey (DiskEncryptionKmsKey string ) bool {
618
- // Validate key against default kmskey pattern
619
- kmsKeyPattern := regexp .MustCompile ("projects/[^/]+/locations/([^/]+)/keyRings/[^/]+/cryptoKeys/[^/]+" )
620
- return kmsKeyPattern .MatchString (DiskEncryptionKmsKey )
621
- }
622
-
623
422
func ParseZoneFromURI (zoneURI string ) (string , error ) {
624
423
zoneMatch := zoneURIRegex .FindStringSubmatch (zoneURI )
625
424
if zoneMatch == nil {
@@ -628,42 +427,6 @@ func ParseZoneFromURI(zoneURI string) (string, error) {
628
427
return zoneMatch [1 ], nil
629
428
}
630
429
631
- // ParseStoragePools returns an error if none of the given storagePools
632
- // (delimited by a comma) are in the format
633
- // projects/project/zones/zone/storagePools/storagePool.
634
- func ParseStoragePools (storagePools string ) ([]StoragePool , error ) {
635
- spSlice := strings .Split (storagePools , "," )
636
- parsedStoragePools := []StoragePool {}
637
- for _ , sp := range spSlice {
638
- project , location , spName , err := fieldsFromStoragePoolResourceName (sp )
639
- if err != nil {
640
- return nil , err
641
- }
642
- spObj := StoragePool {Project : project , Zone : location , Name : spName , ResourceName : sp }
643
- parsedStoragePools = append (parsedStoragePools , spObj )
644
-
645
- }
646
- return parsedStoragePools , nil
647
- }
648
-
649
- // fieldsFromResourceName returns the project, zone, and Storage Pool name from the given
650
- // Storage Pool resource name. The resource name must be in the format
651
- // projects/project/zones/zone/storagePools/storagePool.
652
- // All other formats are invalid, and an error will be returned.
653
- func fieldsFromStoragePoolResourceName (resourceName string ) (project , location , spName string , err error ) {
654
- fieldMatches := storagePoolFieldsRegex .FindStringSubmatch (resourceName )
655
- // Field matches should have 4 strings: [resourceName, project, zone, storagePool]. The first
656
- // match is the entire string.
657
- if len (fieldMatches ) != 4 {
658
- err := fmt .Errorf ("invalid Storage Pool resource name. Got %s, expected projects/project/zones/zone/storagePools/storagePool" , resourceName )
659
- return "" , "" , "" , err
660
- }
661
- project = fieldMatches [1 ]
662
- location = fieldMatches [2 ]
663
- spName = fieldMatches [3 ]
664
- return
665
- }
666
-
667
430
// StoragePoolZones returns the unique zones of the given storage pool resource names.
668
431
// Returns an error if multiple storage pools in 1 zone are found.
669
432
func StoragePoolZones (storagePools []StoragePool ) ([]string , error ) {
@@ -710,29 +473,6 @@ func VolumeIdAsMultiZone(volumeId string) (string, error) {
710
473
return strings .Join (splitId , "/" ), nil
711
474
}
712
475
713
- func StringInSlice (s string , list []string ) bool {
714
- for _ , v := range list {
715
- if v == s {
716
- return true
717
- }
718
- }
719
- return false
720
- }
721
-
722
- func ValidateDataCacheMode (s string ) error {
723
- if StringInSlice (s , validDataCacheMode ) {
724
- return nil
725
- }
726
- return fmt .Errorf ("invalid data-cache-mode %s. Only \" writeback\" and \" writethrough\" is a valid input" , s )
727
- }
728
-
729
- func ValidateNonNegativeInt (n int64 ) error {
730
- if n <= 0 {
731
- return fmt .Errorf ("Input should be set to > 0, got %d" , n )
732
- }
733
- return nil
734
- }
735
-
736
476
// NewLimiter returns a token bucket based request rate limiter after initializing
737
477
// the passed values for limit, burst (or token bucket) size. If opted for emptyBucket
738
478
// all initial tokens are reserved for the first burst.
0 commit comments