istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/envoyfilter/envoyfilter.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 envoyfilter 16 17 import ( 18 "fmt" 19 20 network "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 // EnvoyPatchAnalyzer checks envoyFilters to see if the patch section is okay 30 type EnvoyPatchAnalyzer struct{} 31 32 // (compile-time check that we implement the interface) 33 var _ analysis.Analyzer = &EnvoyPatchAnalyzer{} 34 35 // Metadata implements analysis.Analyzer 36 func (*EnvoyPatchAnalyzer) Metadata() analysis.Metadata { 37 return analysis.Metadata{ 38 Name: "envoyfilter.EnvoyPatchAnalyzer", 39 Description: "Checks an envoyFilters ", 40 Inputs: []config.GroupVersionKind{ 41 gvk.EnvoyFilter, 42 }, 43 } 44 } 45 46 // Analyze implements analysis.Analyzer 47 func (s *EnvoyPatchAnalyzer) Analyze(c analysis.Context) { 48 // hold the filter names that have a proxyVersion set 49 patchFilterNames := make([]string, 0) 50 c.ForEach(gvk.EnvoyFilter, func(r *resource.Instance) bool { 51 names := s.analyzeEnvoyFilterPatch(r, c, patchFilterNames) 52 patchFilterNames = names 53 return true 54 }) 55 } 56 57 func relativeOperationMsg(r *resource.Instance, c analysis.Context, index int, priority int32, patchFilterNames []string, instanceName string) { 58 if priority == 0 { 59 // there is more than one envoy filter that uses the same name where the proxy version 60 // is set and the priority is not set and a relative operator is used. Issue a warning 61 message := msg.NewEnvoyFilterUsesRelativeOperation(r) 62 63 // if the proxyVersion is set choose that error message over the relative operation message as 64 // the proxyVersion error message also indicates that the proxyVersion is set 65 count := 0 66 67 for _, name := range patchFilterNames { 68 if instanceName == name { 69 count++ 70 break 71 } 72 } 73 74 if count > 0 { 75 // there is more than one envoy filter that uses the same name where the proxy version 76 // is set and the priority is not set and a relative operator is used. Issue a warning 77 message = msg.NewEnvoyFilterUsesRelativeOperationWithProxyVersion(r) 78 } 79 80 if line, ok := util.ErrorLine(r, fmt.Sprintf(util.EnvoyFilterConfigPath, index)); ok { 81 message.Line = line 82 } 83 c.Report(gvk.EnvoyFilter, message) 84 85 } 86 } 87 88 func (*EnvoyPatchAnalyzer) analyzeEnvoyFilterPatch(r *resource.Instance, c analysis.Context, patchFilterNames []string) []string { 89 ef := r.Message.(*network.EnvoyFilter) 90 for index, patch := range ef.ConfigPatches { 91 // validate that the patch and match sections are populated 92 if patch.GetPatch() == nil { 93 break 94 } 95 96 // collect filter names to figure out if there is more than one envoyFilter with the same filter name where one 97 // of the envoy filters has the proxy version set 98 instanceName := "" 99 if patch.Patch.GetValue() != nil { 100 if patch.Patch.Value.GetFields() != nil { 101 tmpValue := patch.Patch.Value.GetFields() 102 tmpName := tmpValue["name"] 103 if tmpName != nil { 104 instanceName = tmpValue["name"].String() 105 } else if patch.GetMatch() != nil { 106 if patch.Match.GetListener() != nil { 107 if patch.Match.GetListener().GetFilterChain() != nil { 108 instanceName = patch.Match.GetListener().GetFilterChain().GetFilter().GetName() 109 } 110 } 111 } 112 } 113 } 114 115 // check each operation type 116 if patch.Patch.Operation == network.EnvoyFilter_Patch_ADD { 117 // the ADD operation is an absolute operation but provide a warning 118 // indicating that the operation will be ignored when applyTo is set to ROUTE_CONFIGURATION, 119 // or HTTP_ROUTE 120 if patch.ApplyTo == network.EnvoyFilter_ROUTE_CONFIGURATION || patch.ApplyTo == network.EnvoyFilter_HTTP_ROUTE { 121 // provide an error message indicating a mismatch between the operation type and the filter type 122 message := msg.NewEnvoyFilterUsesAddOperationIncorrectly(r) 123 124 if line, ok := util.ErrorLine(r, fmt.Sprintf(util.EnvoyFilterConfigPath, index)); ok { 125 message.Line = line 126 } 127 128 c.Report(gvk.EnvoyFilter, message) 129 } 130 } else if patch.Patch.Operation == network.EnvoyFilter_Patch_REMOVE { 131 // the REMOVE operation is ignored when applyTo is set to ROUTE_CONFIGURATION, or HTTP_ROUTE. 132 if patch.ApplyTo == network.EnvoyFilter_ROUTE_CONFIGURATION || patch.ApplyTo == network.EnvoyFilter_HTTP_ROUTE { 133 // provide an error message indicating a mismatch between the operation type and the filter type 134 message := msg.NewEnvoyFilterUsesRemoveOperationIncorrectly(r) 135 136 if line, ok := util.ErrorLine(r, fmt.Sprintf(util.EnvoyFilterConfigPath, index)); ok { 137 message.Line = line 138 } 139 140 c.Report(gvk.EnvoyFilter, message) 141 } else { 142 // A relative operation (REMOVE) was used so check if priority is set and if not set provide a warning 143 relativeOperationMsg(r, c, index, ef.Priority, patchFilterNames, instanceName) 144 } 145 } else if patch.Patch.Operation == network.EnvoyFilter_Patch_REPLACE { 146 // the REPLACE operation is only valid for HTTP_FILTER and NETWORK_FILTER. 147 if patch.ApplyTo != network.EnvoyFilter_NETWORK_FILTER && patch.ApplyTo != network.EnvoyFilter_HTTP_FILTER { 148 // provide an error message indicating an invalid filter type 149 message := msg.NewEnvoyFilterUsesReplaceOperationIncorrectly(r) 150 151 if line, ok := util.ErrorLine(r, fmt.Sprintf(util.EnvoyFilterConfigPath, index)); ok { 152 message.Line = line 153 } 154 155 c.Report(gvk.EnvoyFilter, message) 156 } else { 157 // A relative operation (REPLACE) was used so check if priority is set and if not set provide a warning 158 relativeOperationMsg(r, c, index, ef.Priority, patchFilterNames, instanceName) 159 } 160 } else if patch.Patch.Operation == network.EnvoyFilter_Patch_INSERT_BEFORE || patch.Patch.Operation == network.EnvoyFilter_Patch_INSERT_AFTER { 161 // Also a relative operation (INSERT_BEFORE or INSERT_AFTER) was used so check if priority is set and if not set provide a warning 162 relativeOperationMsg(r, c, index, ef.Priority, patchFilterNames, instanceName) 163 } 164 // append the patchValueStr to the slice for next iteration if the proxyVersion is set 165 if patch.GetMatch() != nil { 166 if patch.Match.GetProxy() != nil { 167 if len(patch.Match.Proxy.ProxyVersion) != 0 { 168 patchFilterNames = append(patchFilterNames, instanceName) 169 } 170 } 171 } 172 173 } 174 return patchFilterNames 175 }