package meta import ( "crypto/md5" //nolint:gosec // not used for security "encoding/base64" "fmt" "strings" "unicode" "edge-infra.dev/pkg/lib/gcp/iam" ) // ResourceName creates a valid + unique name for K8s Config Connector IAM // resources that have their name used directly as the name of the corresponding // GCP resource. It could also be used when working directly against the // GCP API, but you could simply rely on the GCP clients to return an error // when an invalid name is provided. // // Due to name limits on some IAM resources in GCP, we need a resource name // under 28 characters. We cant just shorten the id name because names which are // mostly the same will be shortened to the same name and collide within the // namespace // // To simplify code managing groups of these resources (SA, API key, policies), // we typically use the same resource name. // // note on length: // k8s-cfg-conn is our longest resource suffix @ 12 chars, with a max // length of 28 for GCP SA name, we have 16 characters to work with total, // drop that by 4 to give us wiggle room func ResourceName(id string, purpose string) (string, error) { name := fmt.Sprintf("%s-%s", hashName(id, 12), purpose) if ok := iam.ValidAccountID(name); !ok { return "", fmt.Errorf("%s is an invalid service account name", name) } return name, nil } // Hash thinly wraps hashName to generate resource hashes of fixed length func Hash(name string) string { return hashName(name, 12) } // requirements: // - deterministic // - valid kube resource name // - starts with alaphabet character (some GCP resources dont like names that begin with numbers) // - no longer than 12 characters long // - some GCP IAM resources have name limits of 28 characters // - we also need other text in the resource name func hashName(name string, n int) string { // create md5 hash md5Hash := md5.Sum([]byte(name)) //nolint:gosec // not used for security // encode it as base64 to get more character space thana hex hashString := base64.StdEncoding.EncodeToString(md5Hash[:]) var hash []rune // find first n letters for _, r := range hashString { // first letter must be an alphabet character if len(hash) == 0 && !unicode.IsLetter(r) { continue } // dont add characters from the base64 characterspace that are invalid // for k8s resource names if !unicode.IsLetter(r) && !unicode.IsNumber(r) { continue } // exit when we have as many letters as we want if len(hash) == n { break } // add current hash character to final result hash = append(hash, r) } // must be lower case to be a valid kube resource name return strings.ToLower(string(hash)) }