github.com/cilium/cilium@v1.16.2/operator/pkg/model/helpers.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package model 5 6 import ( 7 "crypto/sha256" 8 "fmt" 9 "sort" 10 "strings" 11 12 "github.com/google/go-cmp/cmp" 13 ) 14 15 const ( 16 allHosts = "*" 17 ) 18 19 func AddSource(sourceList []FullyQualifiedResource, source FullyQualifiedResource) []FullyQualifiedResource { 20 for _, s := range sourceList { 21 if cmp.Equal(s, source) { 22 return sourceList 23 } 24 } 25 return append(sourceList, source) 26 } 27 28 // ComputeHosts returns a list of the intersecting hostnames between the route and the listener. 29 // The below function is inspired from https://github.com/envoyproxy/gateway/blob/main/internal/gatewayapi/helpers.go. 30 // Special thanks to Envoy team. 31 // The function takes a list of route hostnames, a listener hostname, and a list of other listener hostnames. 32 // Note that the listenerHostname value will be skipped if it is present in the otherListenerHosts list. 33 func ComputeHosts(routeHostnames []string, listenerHostname *string, otherListenerHosts []string) []string { 34 var listenerHostnameVal string 35 if listenerHostname != nil { 36 listenerHostnameVal = *listenerHostname 37 } 38 39 // No route hostnames specified: use the listener hostname if specified, 40 // or else match all hostnames. 41 if len(routeHostnames) == 0 { 42 if len(listenerHostnameVal) > 0 { 43 return []string{listenerHostnameVal} 44 } 45 46 return []string{allHosts} 47 } 48 49 var hostnames []string 50 51 for i := range routeHostnames { 52 routeHostname := routeHostnames[i] 53 54 switch { 55 // No listener hostname: use the route hostname if there is no overlapping with other listener hostnames. 56 case len(listenerHostnameVal) == 0: 57 if !checkHostNameIsolation(routeHostname, listenerHostnameVal, otherListenerHosts) { 58 hostnames = append(hostnames, routeHostname) 59 } 60 61 // Listener hostname matches the route hostname: use it. 62 case listenerHostnameVal == routeHostname: 63 hostnames = append(hostnames, routeHostname) 64 65 // Listener has a wildcard hostname: check if the route hostname matches. 66 case strings.HasPrefix(listenerHostnameVal, allHosts): 67 if hostnameMatchesWildcardHostname(routeHostname, listenerHostnameVal) && 68 !checkHostNameIsolation(routeHostname, listenerHostnameVal, otherListenerHosts) { 69 hostnames = append(hostnames, routeHostname) 70 } 71 72 // Route has a wildcard hostname: check if the listener hostname matches. 73 case strings.HasPrefix(routeHostname, allHosts): 74 if hostnameMatchesWildcardHostname(listenerHostnameVal, routeHostname) { 75 hostnames = append(hostnames, listenerHostnameVal) 76 } 77 } 78 } 79 80 sort.Strings(hostnames) 81 return hostnames 82 } 83 84 func checkHostNameIsolation(routeHostname string, listenerHostName string, excludedListenerHostnames []string) bool { 85 for _, exHost := range excludedListenerHostnames { 86 if exHost == listenerHostName { 87 continue 88 } 89 if routeHostname == exHost { 90 return true 91 } 92 if strings.HasPrefix(exHost, allHosts) && 93 hostnameMatchesWildcardHostname(routeHostname, exHost) && 94 len(exHost) > len(listenerHostName) { 95 return true 96 } 97 } 98 99 return false 100 } 101 102 // hostnameMatchesWildcardHostname returns true if hostname has the non-wildcard 103 // portion of wildcardHostname as a suffix, plus at least one DNS label matching the 104 // wildcard. 105 func hostnameMatchesWildcardHostname(hostname, wildcardHostname string) bool { 106 if !strings.HasSuffix(hostname, strings.TrimPrefix(wildcardHostname, allHosts)) { 107 return false 108 } 109 110 wildcardMatch := strings.TrimSuffix(hostname, strings.TrimPrefix(wildcardHostname, allHosts)) 111 return len(wildcardMatch) > 0 112 } 113 114 func AddressOf[T any](v T) *T { 115 return &v 116 } 117 118 // Shorten shortens the string to 63 characters. 119 // this is the implicit required for all the resource naming in k8s. 120 func Shorten(s string) string { 121 if len(s) > 63 { 122 return s[:52] + "-" + encodeHash(hash(s)) 123 } 124 return s 125 } 126 127 // encodeHash encodes the first 10 characters of the hex string. 128 // https://github.com/kubernetes/kubernetes/blob/f0dcf0614036d8c3cd1c9f3b3cf8df4bb1d8e44e/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go#L105 129 func encodeHash(hex string) string { 130 enc := []rune(hex[:10]) 131 for i := range enc { 132 switch enc[i] { 133 case '0': 134 enc[i] = 'g' 135 case '1': 136 enc[i] = 'h' 137 case '3': 138 enc[i] = 'k' 139 case 'a': 140 enc[i] = 'm' 141 case 'e': 142 enc[i] = 't' 143 } 144 } 145 return string(enc) 146 } 147 148 // hash hashes `data` with sha256 and returns the hex string 149 func hash(data string) string { 150 return fmt.Sprintf("%x", sha256.Sum256([]byte(data))) 151 }