istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/gateway/conflictinggateway.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 gateway 16 17 import ( 18 "fmt" 19 "sort" 20 "strconv" 21 "strings" 22 23 klabels "k8s.io/apimachinery/pkg/labels" 24 25 "istio.io/api/networking/v1alpha3" 26 "istio.io/istio/pkg/config" 27 "istio.io/istio/pkg/config/analysis" 28 "istio.io/istio/pkg/config/analysis/analyzers/util" 29 "istio.io/istio/pkg/config/analysis/msg" 30 "istio.io/istio/pkg/config/host" 31 "istio.io/istio/pkg/config/resource" 32 "istio.io/istio/pkg/config/schema/gvk" 33 ) 34 35 // ConflictingGatewayAnalyzer checks a gateway's selector, port number and hosts. 36 type ConflictingGatewayAnalyzer struct{} 37 38 // (compile-time check that we implement the interface) 39 var _ analysis.Analyzer = &ConflictingGatewayAnalyzer{} 40 41 // Metadata implements analysis.Analyzer 42 func (*ConflictingGatewayAnalyzer) Metadata() analysis.Metadata { 43 return analysis.Metadata{ 44 Name: "gateway.ConflictingGatewayAnalyzer", 45 Description: "Checks a gateway's selector, port number and hosts", 46 Inputs: []config.GroupVersionKind{ 47 gvk.Gateway, 48 }, 49 } 50 } 51 52 // Analyze implements analysis.Analyzer 53 func (s *ConflictingGatewayAnalyzer) Analyze(c analysis.Context) { 54 gwConflictingMap := initGatewaysMap(c) 55 c.ForEach(gvk.Gateway, func(r *resource.Instance) bool { 56 s.analyzeGateway(r, c, gwConflictingMap) 57 return true 58 }) 59 } 60 61 func (*ConflictingGatewayAnalyzer) analyzeGateway(r *resource.Instance, c analysis.Context, 62 gwCMap map[string]map[string][]string, 63 ) { 64 gw := r.Message.(*v1alpha3.Gateway) 65 gwName := r.Metadata.FullName.String() 66 // For pods selected by gw.Selector, find Services that select them and remember those ports 67 gwSelector := klabels.SelectorFromSet(gw.Selector) 68 sGWSelector := gwSelector.String() 69 70 // Check non-exist gateway with particular selector 71 isExists := false 72 for gwmKey := range gwCMap { 73 if strings.Contains(gwmKey, sGWSelector) { 74 isExists = true 75 break 76 } 77 } 78 if sGWSelector != "" && !isExists { 79 m := msg.NewReferencedResourceNotFound(r, "selector", sGWSelector) 80 label := util.ExtractLabelFromSelectorString(sGWSelector) 81 if line, ok := util.ErrorLine(r, fmt.Sprintf(util.GatewaySelector, label)); ok { 82 m.Line = line 83 } 84 c.Report(gvk.Gateway, m) 85 return 86 } 87 88 for _, server := range gw.Servers { 89 var gateways []string 90 conflictingGWMatch := 0 91 sPortNumber := strconv.Itoa(int(server.GetPort().GetNumber())) 92 mapKey := genGatewayMapKey(sGWSelector, sPortNumber) 93 for gwNameKey, gwHostsValue := range gwCMap[mapKey] { 94 for _, gwHost := range server.GetHosts() { 95 // both selector and portnumber are the same, then check hosts 96 if isGWsHostMatched(gwHost, gwHostsValue) { 97 if gwName != gwNameKey { 98 conflictingGWMatch++ 99 gateways = append(gateways, gwNameKey) 100 } 101 } 102 } 103 } 104 if conflictingGWMatch > 0 { 105 sort.Strings(gateways) 106 reportMsg := strings.Join(gateways, ",") 107 hostsMsg := strings.Join(server.GetHosts(), ",") 108 m := msg.NewConflictingGateways(r, reportMsg, sGWSelector, sPortNumber, hostsMsg) 109 c.Report(gvk.Gateway, m) 110 } 111 } 112 } 113 114 // isGWsHostMatched implements gateway's hosts match 115 func isGWsHostMatched(gwInstance string, gwHostList []string) bool { 116 gwInstanceNamed := host.Name(gwInstance) 117 for _, gwElem := range gwHostList { 118 gwElemNamed := host.Name(gwElem) 119 if gwInstanceNamed.Matches(gwElemNamed) { 120 return true 121 } 122 } 123 return false 124 } 125 126 // initGatewaysMap implements initialization for gateways Map 127 func initGatewaysMap(ctx analysis.Context) map[string]map[string][]string { 128 gwConflictingMap := make(map[string]map[string][]string) 129 ctx.ForEach(gvk.Gateway, func(r *resource.Instance) bool { 130 gw := r.Message.(*v1alpha3.Gateway) 131 gwName := r.Metadata.FullName.String() 132 133 gwSelector := klabels.SelectorFromSet(gw.GetSelector()) 134 sGWSelector := gwSelector.String() 135 for _, server := range gw.GetServers() { 136 sPortNumber := strconv.Itoa(int(server.GetPort().GetNumber())) 137 mapKey := genGatewayMapKey(sGWSelector, sPortNumber) 138 if _, exits := gwConflictingMap[mapKey]; !exits { 139 objMap := make(map[string][]string) 140 objMap[gwName] = server.GetHosts() 141 gwConflictingMap[mapKey] = objMap 142 } else { 143 gwConflictingMap[mapKey][gwName] = server.GetHosts() 144 } 145 } 146 return true 147 }) 148 return gwConflictingMap 149 } 150 151 func genGatewayMapKey(selector, portNumber string) string { 152 key := selector + "~" + portNumber 153 return key 154 }