istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/virtualservice/destinationrules.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 20 "istio.io/api/networking/v1alpha3" 21 "istio.io/istio/pkg/config" 22 "istio.io/istio/pkg/config/analysis" 23 "istio.io/istio/pkg/config/analysis/analyzers/util" 24 "istio.io/istio/pkg/config/analysis/msg" 25 "istio.io/istio/pkg/config/resource" 26 "istio.io/istio/pkg/config/schema/gvk" 27 ) 28 29 // DestinationRuleAnalyzer checks the destination rules associated with each virtual service 30 type DestinationRuleAnalyzer struct{} 31 32 var _ analysis.Analyzer = &DestinationRuleAnalyzer{} 33 34 // Metadata implements Analyzer 35 func (d *DestinationRuleAnalyzer) Metadata() analysis.Metadata { 36 return analysis.Metadata{ 37 Name: "virtualservice.DestinationRuleAnalyzer", 38 Description: "Checks the destination rules associated with each virtual service", 39 Inputs: []config.GroupVersionKind{ 40 gvk.VirtualService, 41 gvk.DestinationRule, 42 }, 43 } 44 } 45 46 // Analyze implements Analyzer 47 func (d *DestinationRuleAnalyzer) Analyze(ctx analysis.Context) { 48 // To avoid repeated iteration, precompute the set of existing destination host+subset combinations 49 destHostsAndSubsets := initDestHostsAndSubsets(ctx) 50 51 ctx.ForEach(gvk.VirtualService, func(r *resource.Instance) bool { 52 d.analyzeVirtualService(r, ctx, destHostsAndSubsets) 53 return true 54 }) 55 } 56 57 func (d *DestinationRuleAnalyzer) analyzeVirtualService(r *resource.Instance, ctx analysis.Context, 58 destHostsAndSubsets map[hostAndSubset]bool, 59 ) { 60 vs := r.Message.(*v1alpha3.VirtualService) 61 ns := r.Metadata.FullName.Namespace 62 63 for _, ad := range getRouteDestinations(vs) { 64 if !d.checkDestinationSubset(ns, ad.Destination, destHostsAndSubsets) { 65 66 m := msg.NewReferencedResourceNotFound(r, "host+subset in destinationrule", 67 fmt.Sprintf("%s+%s", ad.Destination.GetHost(), ad.Destination.GetSubset())) 68 69 key := fmt.Sprintf(util.DestinationHost, ad.RouteRule, ad.ServiceIndex, ad.DestinationIndex) 70 if line, ok := util.ErrorLine(r, key); ok { 71 m.Line = line 72 } 73 74 ctx.Report(gvk.VirtualService, m) 75 } 76 } 77 78 for _, ad := range getHTTPMirrorDestinations(vs) { 79 if !d.checkDestinationSubset(ns, ad.Destination, destHostsAndSubsets) { 80 81 m := msg.NewReferencedResourceNotFound(r, "mirror+subset in destinationrule", 82 fmt.Sprintf("%s+%s", ad.Destination.GetHost(), ad.Destination.GetSubset())) 83 84 var key string 85 if ad.RouteRule == "http.mirror" { 86 key = fmt.Sprintf(util.MirrorHost, ad.ServiceIndex) 87 } else { 88 key = fmt.Sprintf(util.MirrorsHost, ad.ServiceIndex, ad.DestinationIndex) 89 } 90 if line, ok := util.ErrorLine(r, key); ok { 91 m.Line = line 92 } 93 94 ctx.Report(gvk.VirtualService, m) 95 } 96 } 97 } 98 99 func (d *DestinationRuleAnalyzer) checkDestinationSubset(vsNamespace resource.Namespace, destination *v1alpha3.Destination, 100 destHostsAndSubsets map[hostAndSubset]bool, 101 ) bool { 102 name := util.GetResourceNameFromHost(vsNamespace, destination.GetHost()) 103 subset := destination.GetSubset() 104 105 // if there's no subset specified, we're done 106 if subset == "" { 107 return true 108 } 109 110 hs := hostAndSubset{ 111 host: name, 112 subset: subset, 113 } 114 if _, ok := destHostsAndSubsets[hs]; ok { 115 return true 116 } 117 118 return false 119 } 120 121 func initDestHostsAndSubsets(ctx analysis.Context) map[hostAndSubset]bool { 122 hostsAndSubsets := make(map[hostAndSubset]bool) 123 ctx.ForEach(gvk.DestinationRule, func(r *resource.Instance) bool { 124 dr := r.Message.(*v1alpha3.DestinationRule) 125 drNamespace := r.Metadata.FullName.Namespace 126 127 for _, ss := range dr.GetSubsets() { 128 hs := hostAndSubset{ 129 host: util.GetResourceNameFromHost(drNamespace, dr.GetHost()), 130 subset: ss.GetName(), 131 } 132 hostsAndSubsets[hs] = true 133 } 134 return true 135 }) 136 return hostsAndSubsets 137 }