github.com/kiali/kiali@v1.84.0/business/checkers/virtualservices/single_host_checker.go (about) 1 package virtualservices 2 3 import ( 4 networking_v1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" 5 6 "github.com/kiali/kiali/kubernetes" 7 "github.com/kiali/kiali/models" 8 ) 9 10 type SingleHostChecker struct { 11 Cluster string 12 Namespaces models.Namespaces 13 VirtualServices []*networking_v1beta1.VirtualService 14 } 15 16 func (s SingleHostChecker) Check() models.IstioValidations { 17 hostCounter := make(map[string]map[string]map[string]map[string][]*networking_v1beta1.VirtualService) 18 validations := models.IstioValidations{} 19 20 for _, vs := range s.VirtualServices { 21 for _, host := range s.getHosts(vs) { 22 storeHost(hostCounter, vs, host) 23 } 24 } 25 26 for _, gateways := range hostCounter { 27 for _, clusterCounter := range gateways { 28 for _, namespaceCounter := range clusterCounter { 29 for _, serviceCounter := range namespaceCounter { 30 isNamespaceWildcard := len(namespaceCounter["*"]) > 0 31 targetSameHost := len(serviceCounter) > 1 32 otherServiceHosts := len(namespaceCounter) > 1 33 for _, virtualService := range serviceCounter { 34 // Marking virtualService as invalid if: 35 // - there is more than one virtual service per a host 36 // - there is one virtual service with wildcard and there are other virtual services pointing 37 // a host for that namespace 38 if targetSameHost { 39 // Reference everything within serviceCounter 40 multipleVirtualServiceCheck(virtualService, validations, serviceCounter, s.Cluster) 41 } 42 43 if isNamespaceWildcard && otherServiceHosts { 44 // Reference the * or in case of * the other hosts inside namespace 45 // or other stars 46 refs := make([]*networking_v1beta1.VirtualService, 0, len(namespaceCounter)) 47 // here in case of a, b and *, references should be a -> *, b -> *, * -> q,b 48 // * should be referenced to a,b 49 if containsVirtualService(virtualService, namespaceCounter["*"]) { 50 for _, _serviceCounter := range namespaceCounter { 51 refs = append(refs, _serviceCounter...) 52 } 53 } else { 54 // a or b referencing to * 55 refs = append(refs, namespaceCounter["*"]...) 56 } 57 multipleVirtualServiceCheck(virtualService, validations, refs, s.Cluster) 58 } 59 } 60 } 61 } 62 } 63 } 64 65 return validations 66 } 67 68 func containsVirtualService(vs *networking_v1beta1.VirtualService, vss []*networking_v1beta1.VirtualService) bool { 69 for _, item := range vss { 70 if vs.Name == item.Name && vs.Namespace == item.Namespace { 71 return true 72 } 73 } 74 return false 75 } 76 77 func multipleVirtualServiceCheck(virtualService *networking_v1beta1.VirtualService, validations models.IstioValidations, references []*networking_v1beta1.VirtualService, cluster string) { 78 virtualServiceName := virtualService.Name 79 key := models.IstioValidationKey{Name: virtualServiceName, Namespace: virtualService.Namespace, ObjectType: "virtualservice", Cluster: cluster} 80 checks := models.Build("virtualservices.singlehost", "spec/hosts") 81 rrValidation := &models.IstioValidation{ 82 Name: virtualServiceName, 83 ObjectType: "virtualservice", 84 Valid: true, 85 Checks: []*models.IstioCheck{ 86 &checks, 87 }, 88 References: make([]models.IstioValidationKey, 0, len(references)), 89 } 90 91 for _, ref := range references { 92 refKey := models.IstioValidationKey{Name: ref.Name, Namespace: ref.Namespace, ObjectType: "virtualservice", Cluster: cluster} 93 if refKey != key { 94 rrValidation.References = append(rrValidation.References, refKey) 95 } 96 } 97 98 validations.MergeValidations(models.IstioValidations{key: rrValidation}) 99 } 100 101 func storeHost(hostCounter map[string]map[string]map[string]map[string][]*networking_v1beta1.VirtualService, vs *networking_v1beta1.VirtualService, host kubernetes.Host) { 102 vsList := []*networking_v1beta1.VirtualService{vs} 103 104 gwList := vs.Spec.Gateways 105 if len(gwList) == 0 { 106 gwList = []string{"no-gateway"} 107 } 108 109 cluster := host.Cluster 110 namespace := host.Namespace 111 service := host.Service 112 113 for _, gw := range gwList { 114 if hostCounter[gw] == nil { 115 hostCounter[gw] = map[string]map[string]map[string][]*networking_v1beta1.VirtualService{ 116 cluster: { 117 namespace: { 118 service: vsList, 119 }, 120 }, 121 } 122 } else if hostCounter[gw][cluster] == nil { 123 hostCounter[gw][cluster] = map[string]map[string][]*networking_v1beta1.VirtualService{ 124 namespace: { 125 service: vsList, 126 }, 127 } 128 } else if hostCounter[gw][cluster][namespace] == nil { 129 hostCounter[gw][cluster][namespace] = map[string][]*networking_v1beta1.VirtualService{ 130 service: vsList, 131 } 132 } else if _, ok := hostCounter[gw][cluster][namespace][service]; !ok { 133 hostCounter[gw][cluster][namespace][service] = vsList 134 } else { 135 hostCounter[gw][cluster][namespace][service] = append(hostCounter[gw][cluster][namespace][service], vs) 136 } 137 } 138 } 139 140 func (s SingleHostChecker) getHosts(virtualService *networking_v1beta1.VirtualService) []kubernetes.Host { 141 namespace := virtualService.Namespace 142 143 if len(virtualService.Spec.Hosts) == 0 { 144 return []kubernetes.Host{} 145 } 146 147 targetHosts := make([]kubernetes.Host, 0, len(virtualService.Spec.Hosts)) 148 149 for _, hostName := range virtualService.Spec.Hosts { 150 targetHosts = append(targetHosts, kubernetes.GetHost(hostName, namespace, s.Namespaces.GetNames())) 151 } 152 return targetHosts 153 }