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  }