cmd/gcpNameResolver.go (112 lines of code) (raw):

package cmd import ( "bytes" "fmt" "strconv" "strings" ) //For GCS, container naming follows: //1. Bucket names must contain only lowercase letters, numbers, dashes (-), underscores (_), and dots (.). //Spaces are not allowed. Names containing dots require verification. //2. Bucket names must start and end with a number or letter. //3. Bucket names must contain 3-63 characters. Names containing dots can contain up to 222 characters, //but each dot-separated component can be no longer than 63 characters. //4. Bucket names cannot be represented as an IP address in dotted-decimal notation (for example, 192.168.5.4). //5. Bucket names cannot begin with the "goog" prefix. //6. Bucket names cannot contain "google" or close misspellings, such as "g00gle". //For Azure, container/filesystem/share's naming follows: //1. Lower case letters, numbers and hyphen. //2. 3-63 length. //3. Name should not contain two consecutive hyphens. //4. Name should not start or end with hyphen. //In general we only have to fix the following cases: //1. Names with underscores (_). Here we will try to replace it with hyphens (-). // e.g. bucket_name -> bucket-name //2. Names with consecutive hyphens. Here we will replace it with -[number of hyphens]-. // e.g. bucket-----name -> bucket-5-name. //3. Names with period (.). Here we will replace it with hyphen (-). //4. In case of any naming collisions we try to add a suffix. // e.g. Bucket names: bucket-name, bucket.name. Then we will resolve into bucket.name -> bucket-name -> bucket-name-2 type GCPBucketNameToAzureResourcesResolver struct { bucketNameResolvingMap map[string]string collisionDetectionMap map[string]struct{} } const gcpBucketNameMaxLength = 63 var gcpBucketNameResolveError = "fail to resolve GCP bucket name" func NewGCPBucketNameToAzureResourcesResolver(gcpBucketNames []string) *GCPBucketNameToAzureResourcesResolver { gcpResolver := GCPBucketNameToAzureResourcesResolver{ bucketNameResolvingMap: make(map[string]string), collisionDetectionMap: make(map[string]struct{}), } for _, bucketName := range gcpBucketNames { gcpResolver.bucketNameResolvingMap[bucketName] = "" } for _, bucketName := range gcpBucketNames { _, _ = gcpResolver.ResolveName(bucketName) } return &gcpResolver } func (resolver *GCPBucketNameToAzureResourcesResolver) ResolveName(bucketName string) (string, error) { if resolvedName, ok := resolver.bucketNameResolvingMap[bucketName]; !ok || resolvedName == "" { resolver.bucketNameResolvingMap[bucketName] = bucketName resolver.resolveNewBucketNameInternal(bucketName) return resolver.ResolveName(bucketName) } else if resolvedName == failToResolveMapValue { return "", fmt.Errorf("%s: container name %q is invalid for destination.", gcpBucketNameResolveError, bucketName) } else { return resolvedName, nil } } func (resolver *GCPBucketNameToAzureResourcesResolver) resolveNewBucketNameInternal(bucketName string) { hasPeriod := strings.Contains(bucketName, ".") hasUnderscores := strings.Contains(bucketName, "_") hasConsecutiveHyphens := strings.Contains(bucketName, "--") if !hasPeriod && !hasUnderscores && !hasConsecutiveHyphens { return } resolvedName := bucketName //Try to replace (.) with (-) if hasPeriod { resolvedName = strings.Replace(resolvedName, ".", "-", -1) } //Try to replace (_) with (-) if hasUnderscores { resolvedName = strings.Replace(resolvedName, "_", "-", -1) } hasConsecutiveHyphens = strings.Contains(resolvedName, "--") //Try to replace consecutive hyphens (--) with number of hyphens if hasConsecutiveHyphens { var buffer bytes.Buffer consecutiveHyphenCount := 0 for i := 0; i < len(resolvedName); i++ { charAtI := resolvedName[i] if charAtI == '-' { consecutiveHyphenCount++ continue } if consecutiveHyphenCount == 0 { buffer.WriteByte(charAtI) } else if consecutiveHyphenCount == 1 { buffer.WriteString("-") buffer.WriteByte(charAtI) } else { buffer.WriteString("-") buffer.WriteString(strconv.Itoa(consecutiveHyphenCount)) buffer.WriteString("-") buffer.WriteByte(charAtI) } consecutiveHyphenCount = 0 } if consecutiveHyphenCount > 0 { panic("Invalid value for consecutiveHyphenCount. Value cannot be greater than 0") } resolvedName = buffer.String() } if resolver.hasCollision(resolvedName) { resolvedName = resolver.addSuffix(resolvedName) } if !validateResolvedNameGCP(resolvedName) { resolvedName = failToResolveMapValue } resolver.bucketNameResolvingMap[bucketName] = resolvedName resolver.collisionDetectionMap[resolvedName] = struct{}{} } func (resolver *GCPBucketNameToAzureResourcesResolver) hasCollision(name string) bool { _, hasCollisionToOrgNames := resolver.bucketNameResolvingMap[name] _, hasCollisionToResolvedNames := resolver.collisionDetectionMap[name] return hasCollisionToOrgNames || hasCollisionToResolvedNames } func validateResolvedNameGCP(name string) bool { if len(name) > gcpBucketNameMaxLength { return false } return true } func (resolver *GCPBucketNameToAzureResourcesResolver) addSuffix(bucketName string) string { suffixPattern := "%s-%d" count := 2 resolvedName := fmt.Sprintf(suffixPattern, bucketName, count) for { if !resolver.hasCollision(resolvedName) { break } count++ resolvedName = fmt.Sprintf(suffixPattern, bucketName, count) } return resolvedName }