knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/kmeta/names.go (about)

     1  /*
     2  copyright 2019 the knative authors
     3  
     4  licensed under the apache license, version 2.0 (the "license");
     5  you may not use this file except in compliance with the license.
     6  you may obtain a copy of the license at
     7  
     8      http://www.apache.org/licenses/license-2.0
     9  
    10  unless required by applicable law or agreed to in writing, software
    11  distributed under the license is distributed on an "as is" basis,
    12  without warranties or conditions of any kind, either express or implied.
    13  see the license for the specific language governing permissions and
    14  limitations under the license.
    15  */
    16  
    17  package kmeta
    18  
    19  import (
    20  	"crypto/md5" //nolint:gosec // No strong cryptography needed.
    21  	"encoding/hex"
    22  	"fmt"
    23  	"regexp"
    24  )
    25  
    26  // The longest name supported by the K8s is 63.
    27  // These constants
    28  const (
    29  	longest = 63
    30  	md5Len  = 32
    31  	head    = longest - md5Len // How much to truncate to fit the hash.
    32  )
    33  
    34  var isAlphanumeric = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
    35  
    36  // ChildName generates a name for the resource based upon the parent resource and suffix.
    37  // If the concatenated name is longer than K8s permits the name is hashed and truncated to permit
    38  // construction of the resource, but still keeps it unique.
    39  // If the suffix itself is longer than 31 characters, then the whole string will be hashed
    40  // and `parent|hash|suffix` will be returned, where parent and suffix will be trimmed to
    41  // fit (prefix of parent at most of length 31, and prefix of suffix at most length 30).
    42  func ChildName(parent, suffix string) string {
    43  	n := parent
    44  	if len(parent) > (longest - len(suffix)) {
    45  		// If the suffix is longer than the longest allowed suffix, then
    46  		// we hash the whole combined string and use that as the suffix.
    47  		if head-len(suffix) <= 0 {
    48  			//nolint:gosec // No strong cryptography needed.
    49  			h := md5.Sum([]byte(parent + suffix))
    50  			// 1. trim parent, if needed
    51  			if head < len(parent) {
    52  				parent = parent[:head]
    53  			}
    54  			// Format the return string, if it's shorter than longest: pad with
    55  			// beginning of the suffix. This happens, for example, when parent is
    56  			// short, but the suffix is very long.
    57  			ret := parent + hex.EncodeToString(h[:])
    58  			if d := longest - len(ret); d > 0 {
    59  				ret += suffix[:d]
    60  			}
    61  			return makeValidName(ret)
    62  		}
    63  		//nolint:gosec // No strong cryptography needed.
    64  		n = fmt.Sprintf("%s%x", parent[:head-len(suffix)], md5.Sum([]byte(parent)))
    65  	}
    66  	return n + suffix
    67  }
    68  
    69  // If due to trimming above we're terminating the string with a non-alphanumeric
    70  // character, remove it.
    71  func makeValidName(n string) string {
    72  	for i := len(n) - 1; !isAlphanumeric.MatchString(string(n[i])); i-- {
    73  		n = n[:len(n)-1]
    74  	}
    75  	return n
    76  }