github.com/kiali/kiali@v1.84.0/business/checkers/k8sgateways/multi_match_checker.go (about) 1 package k8sgateways 2 3 import ( 4 "fmt" 5 6 k8s_networking_v1 "sigs.k8s.io/gateway-api/apis/v1" 7 8 "github.com/kiali/kiali/models" 9 ) 10 11 type MultiMatchChecker struct { 12 Cluster string 13 K8sGateways []*k8s_networking_v1.Gateway 14 } 15 16 const ( 17 K8sGatewayCheckerType = "k8sgateway" 18 ) 19 20 // Check validates that no two gateways share the same host+port combination 21 func (m MultiMatchChecker) Check() models.IstioValidations { 22 validations := models.IstioValidations{} 23 24 for _, g := range m.K8sGateways { 25 gatewayRuleName := g.Name 26 gatewayNamespace := g.Namespace 27 28 // With addresses 29 for _, address := range g.Spec.Addresses { 30 duplicate, collidingGateways := m.findMatchIP(address, g.Name) 31 if duplicate { 32 // The above is referenced by each one below.. 33 currentHostValidation := createError(gatewayRuleName, "k8sgateways.multimatch.ip", gatewayNamespace, m.Cluster, "spec/addresses/value", collidingGateways) 34 validations = validations.MergeValidations(currentHostValidation) 35 } 36 } 37 38 // With listeners 39 for index, listener := range g.Spec.Listeners { 40 duplicate, collidingGateways := m.findMatch(listener, g.Name) 41 // Find in a different k8s GW 42 if duplicate { 43 // The above is referenced by each one below.. 44 currentHostValidation := createError(gatewayRuleName, "k8sgateways.multimatch.listener", gatewayNamespace, m.Cluster, fmt.Sprintf("spec/listeners[%d]/hostname", index), collidingGateways) 45 validations = validations.MergeValidations(currentHostValidation) 46 } 47 // Check for unique listeners in the GW 48 for i, l := range g.Spec.Listeners { 49 if listener.Name != l.Name && l.Hostname != nil && listener.Hostname != nil && *listener.Hostname == *l.Hostname && listener.Port == l.Port && listener.Protocol == l.Protocol { 50 currentHostValidation := createError(gatewayRuleName, "k8sgateways.unique.listener", gatewayNamespace, m.Cluster, fmt.Sprintf("spec/listeners[%d]/name", i), nil) 51 validations = validations.MergeValidations(currentHostValidation) 52 } 53 } 54 } 55 } 56 57 return validations 58 } 59 60 // Create validation error for k8sgateway object 61 func createError(gatewayRuleName string, ruleCode string, namespace string, cluster string, path string, references []models.IstioValidationKey) models.IstioValidations { 62 key := models.IstioValidationKey{Name: gatewayRuleName, Namespace: namespace, ObjectType: K8sGatewayCheckerType, Cluster: cluster} 63 checks := models.Build(ruleCode, path) 64 rrValidation := &models.IstioValidation{ 65 Name: gatewayRuleName, 66 ObjectType: K8sGatewayCheckerType, 67 Valid: true, 68 Checks: []*models.IstioCheck{ 69 &checks, 70 }, 71 References: references, 72 } 73 74 return models.IstioValidations{key: rrValidation} 75 } 76 77 // findMatch uses a linear search with regexp to check for matching gateway host + port combinations. If this becomes a bottleneck for performance, replace with a graph or trie algorithm. 78 func (m MultiMatchChecker) findMatch(listener k8s_networking_v1.Listener, gwName string) (bool, []models.IstioValidationKey) { 79 collidingGateways := make([]models.IstioValidationKey, 0) 80 81 for _, gw := range m.K8sGateways { 82 if gw.Name == gwName { 83 continue 84 } 85 for _, l := range gw.Spec.Listeners { 86 if l.Hostname != nil && listener.Hostname != nil && *l.Hostname == *listener.Hostname && l.Port == listener.Port && l.Protocol == listener.Protocol { 87 key := models.IstioValidationKey{Name: gw.Name, Namespace: gw.Namespace, ObjectType: K8sGatewayCheckerType} 88 collidingGateways = append(collidingGateways, key) 89 } 90 } 91 92 } 93 return len(collidingGateways) > 0, collidingGateways 94 } 95 96 // Check duplicates IP 97 func (m MultiMatchChecker) findMatchIP(address k8s_networking_v1.GatewayAddress, gwName string) (bool, []models.IstioValidationKey) { 98 collidingGateways := make([]models.IstioValidationKey, 0) 99 100 for _, aa := range m.K8sGateways { 101 if aa.Name == gwName { 102 continue 103 } 104 105 for _, a := range aa.Spec.Addresses { 106 if a.Type != nil && address.Type != nil && *a.Type == *address.Type && a.Value == address.Value { 107 key := models.IstioValidationKey{Name: aa.Name, Namespace: aa.Namespace, ObjectType: K8sGatewayCheckerType} 108 collidingGateways = append(collidingGateways, key) 109 } 110 } 111 } 112 return len(collidingGateways) > 0, collidingGateways 113 }