Skip to content

Commit eac2b7f

Browse files
committed
new packages pkg/parameters, pkg/convert
1 parent 6234714 commit eac2b7f

File tree

16 files changed

+1144
-1631
lines changed

16 files changed

+1144
-1631
lines changed

pkg/common/utils.go

Lines changed: 0 additions & 306 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ import (
3434
"google.golang.org/api/googleapi"
3535
"google.golang.org/grpc/codes"
3636
"google.golang.org/grpc/status"
37-
"k8s.io/apimachinery/pkg/api/resource"
3837
"k8s.io/apimachinery/pkg/util/sets"
39-
volumehelpers "k8s.io/cloud-provider/volume/helpers"
4038
"k8s.io/klog/v2"
4139
"sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/constants"
4240
)
@@ -91,15 +89,10 @@ const (
9189
)
9290

9391
var (
94-
multiRegionalPattern = regexp.MustCompile(multiRegionalLocationFmt)
95-
regionalPattern = regexp.MustCompile(regionalLocationFmt)
96-
9792
// Full or partial URL of the machine type resource, in the format:
9893
// zones/zone/machineTypes/machine-type
9994
machineTypeRegex = regexp.MustCompile(machineTypePattern)
10095

101-
storagePoolFieldsRegex = regexp.MustCompile(`^projects/([^/]+)/zones/([^/]+)/storagePools/([^/]+)$`)
102-
10396
zoneURIRegex = regexp.MustCompile(zoneURIPattern)
10497

10598
// userErrorCodeMap tells how API error types are translated to error codes.
@@ -111,13 +104,6 @@ var (
111104
http.StatusConflict: codes.FailedPrecondition,
112105
}
113106

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-
121107
csiRetryableErrorCodes = []codes.Code{codes.Canceled, codes.DeadlineExceeded, codes.Unavailable, codes.Aborted, codes.ResourceExhausted}
122108
)
123109

@@ -234,212 +220,6 @@ func CreateZonalVolumeID(project, zone, name string) string {
234220
return fmt.Sprintf(volIDZonalFmt, project, zone, name)
235221
}
236222

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-
// ValidateSnapshotType validates the type
385-
func ValidateSnapshotType(snapshotType string) error {
386-
switch snapshotType {
387-
case DiskSnapshotType, DiskImageType:
388-
return nil
389-
default:
390-
return fmt.Errorf("invalid snapshot type %s", snapshotType)
391-
}
392-
}
393-
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-
443223
// ParseMachineType returns an extracted machineType from a URL, or empty if not found.
444224
// machineTypeUrl: Full or partial URL of the machine type resource, in the format:
445225
//
@@ -614,12 +394,6 @@ func NewCombinedError(msg string, errs []error) error {
614394
return LoggedError(msg, errors.Join(errs...))
615395
}
616396

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-
623397
func ParseZoneFromURI(zoneURI string) (string, error) {
624398
zoneMatch := zoneURIRegex.FindStringSubmatch(zoneURI)
625399
if zoneMatch == nil {
@@ -628,65 +402,8 @@ func ParseZoneFromURI(zoneURI string) (string, error) {
628402
return zoneMatch[1], nil
629403
}
630404

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-
667405
// StoragePoolZones returns the unique zones of the given storage pool resource names.
668406
// Returns an error if multiple storage pools in 1 zone are found.
669-
func StoragePoolZones(storagePools []StoragePool) ([]string, error) {
670-
zonesSet := sets.String{}
671-
var zones []string
672-
for _, sp := range storagePools {
673-
if zonesSet.Has(sp.Zone) {
674-
return nil, fmt.Errorf("found multiple storage pools in zone %s. Only one storage pool per zone is allowed", sp.Zone)
675-
}
676-
zonesSet.Insert(sp.Zone)
677-
zones = append(zones, sp.Zone)
678-
}
679-
return zones, nil
680-
}
681-
682-
func StoragePoolInZone(storagePools []StoragePool, zone string) *StoragePool {
683-
for _, pool := range storagePools {
684-
if zone == pool.Zone {
685-
return &pool
686-
}
687-
}
688-
return nil
689-
}
690407

691408
func UnorderedSlicesEqual(slice1 []string, slice2 []string) bool {
692409
set1 := sets.NewString(slice1...)
@@ -710,29 +427,6 @@ func VolumeIdAsMultiZone(volumeId string) (string, error) {
710427
return strings.Join(splitId, "/"), nil
711428
}
712429

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-
736430
// NewLimiter returns a token bucket based request rate limiter after initializing
737431
// the passed values for limit, burst (or token bucket) size. If opted for emptyBucket
738432
// all initial tokens are reserved for the first burst.

0 commit comments

Comments
 (0)