1 package meta 2 3 import ( 4 "crypto/md5" //nolint:gosec // not used for security 5 "encoding/base64" 6 "fmt" 7 "strings" 8 "unicode" 9 10 "edge-infra.dev/pkg/lib/gcp/iam" 11 ) 12 13 // ResourceName creates a valid + unique name for K8s Config Connector IAM 14 // resources that have their name used directly as the name of the corresponding 15 // GCP resource. It could also be used when working directly against the 16 // GCP API, but you could simply rely on the GCP clients to return an error 17 // when an invalid name is provided. 18 // 19 // Due to name limits on some IAM resources in GCP, we need a resource name 20 // under 28 characters. We cant just shorten the id name because names which are 21 // mostly the same will be shortened to the same name and collide within the 22 // namespace 23 // 24 // To simplify code managing groups of these resources (SA, API key, policies), 25 // we typically use the same resource name. 26 // 27 // note on length: 28 // k8s-cfg-conn is our longest resource suffix @ 12 chars, with a max 29 // length of 28 for GCP SA name, we have 16 characters to work with total, 30 // drop that by 4 to give us wiggle room 31 func ResourceName(id string, purpose string) (string, error) { 32 name := fmt.Sprintf("%s-%s", hashName(id, 12), purpose) 33 if ok := iam.ValidAccountID(name); !ok { 34 return "", fmt.Errorf("%s is an invalid service account name", name) 35 } 36 return name, nil 37 } 38 39 // Hash thinly wraps hashName to generate resource hashes of fixed length 40 func Hash(name string) string { 41 return hashName(name, 12) 42 } 43 44 // requirements: 45 // - deterministic 46 // - valid kube resource name 47 // - starts with alaphabet character (some GCP resources dont like names that begin with numbers) 48 // - no longer than 12 characters long 49 // - some GCP IAM resources have name limits of 28 characters 50 // - we also need other text in the resource name 51 func hashName(name string, n int) string { 52 // create md5 hash 53 md5Hash := md5.Sum([]byte(name)) //nolint:gosec // not used for security 54 // encode it as base64 to get more character space thana hex 55 hashString := base64.StdEncoding.EncodeToString(md5Hash[:]) 56 57 var hash []rune 58 // find first n letters 59 for _, r := range hashString { 60 // first letter must be an alphabet character 61 if len(hash) == 0 && !unicode.IsLetter(r) { 62 continue 63 } 64 // dont add characters from the base64 characterspace that are invalid 65 // for k8s resource names 66 if !unicode.IsLetter(r) && !unicode.IsNumber(r) { 67 continue 68 } 69 // exit when we have as many letters as we want 70 if len(hash) == n { 71 break 72 } 73 // add current hash character to final result 74 hash = append(hash, r) 75 } 76 77 // must be lower case to be a valid kube resource name 78 return strings.ToLower(string(hash)) 79 } 80