istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/virtualservice/conflictingmeshgatewayhosts.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package virtualservice 16 17 import ( 18 "fmt" 19 "sort" 20 "strings" 21 22 "istio.io/api/networking/v1alpha3" 23 "istio.io/istio/pkg/config" 24 "istio.io/istio/pkg/config/analysis" 25 "istio.io/istio/pkg/config/analysis/analyzers/util" 26 "istio.io/istio/pkg/config/analysis/msg" 27 "istio.io/istio/pkg/config/host" 28 "istio.io/istio/pkg/config/resource" 29 "istio.io/istio/pkg/config/schema/gvk" 30 "istio.io/istio/pkg/util/sets" 31 ) 32 33 // ConflictingMeshGatewayHostsAnalyzer checks if multiple virtual services 34 // associated with the mesh gateway have conflicting hosts. The behavior is 35 // undefined if conflicts exist. 36 type ConflictingMeshGatewayHostsAnalyzer struct{} 37 38 var _ analysis.Analyzer = &ConflictingMeshGatewayHostsAnalyzer{} 39 40 // Metadata implements Analyzer 41 func (c *ConflictingMeshGatewayHostsAnalyzer) Metadata() analysis.Metadata { 42 return analysis.Metadata{ 43 Name: "virtualservice.ConflictingMeshGatewayHostsAnalyzer", 44 Description: "Checks if multiple virtual services associated with the mesh gateway have conflicting hosts", 45 Inputs: []config.GroupVersionKind{ 46 gvk.VirtualService, 47 }, 48 } 49 } 50 51 // Analyze implements Analyzer 52 func (c *ConflictingMeshGatewayHostsAnalyzer) Analyze(ctx analysis.Context) { 53 hs := initMeshGatewayHosts(ctx) 54 reported := make(map[resource.FullName]bool) 55 for scopedFqdn, vsList := range hs { 56 scope, _ := scopedFqdn.GetScopeAndFqdn() 57 if scope != util.ExportToAllNamespaces { 58 noScopedVSList := getExportToAllNamespacesVSListForScopedHost(scopedFqdn, hs) 59 vsList = append(vsList, noScopedVSList...) 60 } 61 if len(vsList) > 1 { 62 vsNames := combineResourceEntryNames(vsList) 63 for i := range vsList { 64 if reported[vsList[i].Metadata.FullName] { 65 continue 66 } 67 reported[vsList[i].Metadata.FullName] = true 68 m := msg.NewConflictingMeshGatewayVirtualServiceHosts(vsList[i], vsNames, string(scopedFqdn)) 69 70 if line, ok := util.ErrorLine(vsList[i], fmt.Sprintf(util.MetadataName)); ok { 71 m.Line = line 72 } 73 74 ctx.Report(gvk.VirtualService, m) 75 } 76 } 77 78 } 79 } 80 81 func getExportToAllNamespacesVSListForScopedHost(sh util.ScopedFqdn, meshGatewayHosts map[util.ScopedFqdn][]*resource.Instance) []*resource.Instance { 82 _, h := sh.GetScopeAndFqdn() 83 vss := make([]*resource.Instance, 0) 84 for sf, resources := range meshGatewayHosts { 85 mghScope, mgh := sf.GetScopeAndFqdn() 86 hName := host.Name(h) 87 mghName := host.Name(mgh) 88 if mghScope != util.ExportToAllNamespaces || !hName.Matches(mghName) { 89 continue 90 } 91 vss = append(vss, resources...) 92 } 93 return vss 94 } 95 96 func combineResourceEntryNames(rList []*resource.Instance) string { 97 names := make([]string, 0, len(rList)) 98 for _, r := range rList { 99 names = append(names, r.Metadata.FullName.String()) 100 } 101 sort.Strings(names) 102 return strings.Join(names, ",") 103 } 104 105 func initMeshGatewayHosts(ctx analysis.Context) map[util.ScopedFqdn][]*resource.Instance { 106 hostsVirtualServices := map[util.ScopedFqdn][]*resource.Instance{} 107 ctx.ForEach(gvk.VirtualService, func(r *resource.Instance) bool { 108 vs := r.Message.(*v1alpha3.VirtualService) 109 vsNamespace := r.Metadata.FullName.Namespace 110 vsAttachedToMeshGateway := false 111 // No entry in gateways imply "mesh" by default 112 if len(vs.Gateways) == 0 { 113 vsAttachedToMeshGateway = true 114 } else { 115 for _, g := range vs.Gateways { 116 if g == util.MeshGateway { 117 vsAttachedToMeshGateway = true 118 } 119 } 120 } 121 if vsAttachedToMeshGateway { 122 // determine the scope of hosts i.e. local to VirtualService namespace or 123 // all namespaces 124 hostsNamespaceScope := make([]string, 0) 125 exportToAllNamespaces := util.IsExportToAllNamespaces(vs.ExportTo) 126 if exportToAllNamespaces { 127 hostsNamespaceScope = append(hostsNamespaceScope, util.ExportToAllNamespaces) 128 } else { 129 nss := sets.New[string]() 130 for _, et := range vs.ExportTo { 131 if et == util.ExportToNamespaceLocal { 132 nss.Insert(vsNamespace.String()) 133 } else { 134 nss.Insert(et) 135 } 136 } 137 hostsNamespaceScope = nss.UnsortedList() 138 } 139 140 for _, nsScope := range hostsNamespaceScope { 141 for _, h := range vs.Hosts { 142 scopedFqdn := util.NewScopedFqdn(nsScope, vsNamespace, h) 143 vsNames := hostsVirtualServices[scopedFqdn] 144 if len(vsNames) == 0 { 145 hostsVirtualServices[scopedFqdn] = []*resource.Instance{r} 146 } else { 147 hostsVirtualServices[scopedFqdn] = append(hostsVirtualServices[scopedFqdn], r) 148 } 149 } 150 } 151 } 152 return true 153 }) 154 return hostsVirtualServices 155 }