...

Source file src/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/unmarshal_lds_test.go

Documentation: google.golang.org/grpc/xds/internal/xdsclient/xdsresource

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package xdsresource
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
    27  	"github.com/google/go-cmp/cmp"
    28  	"google.golang.org/grpc/internal/pretty"
    29  	"google.golang.org/grpc/internal/testutils"
    30  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    31  	"google.golang.org/grpc/xds/internal/httpfilter"
    32  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
    33  	"google.golang.org/protobuf/proto"
    34  	"google.golang.org/protobuf/types/known/anypb"
    35  	"google.golang.org/protobuf/types/known/durationpb"
    36  	"google.golang.org/protobuf/types/known/structpb"
    37  	"google.golang.org/protobuf/types/known/wrapperspb"
    38  
    39  	v1xdsudpatypepb "github.com/cncf/xds/go/udpa/type/v1"
    40  	v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3"
    41  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    42  	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    43  	rpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
    44  	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    45  	v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3"
    46  	v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
    47  	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    48  	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    49  	v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
    50  
    51  	_ "google.golang.org/grpc/xds/internal/httpfilter/rbac"   // Register the RBAC HTTP filter.
    52  	_ "google.golang.org/grpc/xds/internal/httpfilter/router" // Register the router filter.
    53  )
    54  
    55  func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
    56  	const (
    57  		v3LDSTarget       = "lds.target.good:3333"
    58  		v3RouteConfigName = "v3RouteConfig"
    59  		routeName         = "routeName"
    60  	)
    61  
    62  	var (
    63  		customFilter = &v3httppb.HttpFilter{
    64  			Name:       "customFilter",
    65  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    66  		}
    67  		oldTypedStructFilter = &v3httppb.HttpFilter{
    68  			Name:       "customFilter",
    69  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: testutils.MarshalAny(t, customFilterOldTypedStructConfig)},
    70  		}
    71  		newTypedStructFilter = &v3httppb.HttpFilter{
    72  			Name:       "customFilter",
    73  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: testutils.MarshalAny(t, customFilterNewTypedStructConfig)},
    74  		}
    75  		customOptionalFilter = &v3httppb.HttpFilter{
    76  			Name:       "customFilter",
    77  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    78  			IsOptional: true,
    79  		}
    80  		customFilter2 = &v3httppb.HttpFilter{
    81  			Name:       "customFilter2",
    82  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    83  		}
    84  		errFilter = &v3httppb.HttpFilter{
    85  			Name:       "errFilter",
    86  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
    87  		}
    88  		errOptionalFilter = &v3httppb.HttpFilter{
    89  			Name:       "errFilter",
    90  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
    91  			IsOptional: true,
    92  		}
    93  		clientOnlyCustomFilter = &v3httppb.HttpFilter{
    94  			Name:       "clientOnlyCustomFilter",
    95  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig},
    96  		}
    97  		serverOnlyCustomFilter = &v3httppb.HttpFilter{
    98  			Name:       "serverOnlyCustomFilter",
    99  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   100  		}
   101  		serverOnlyOptionalCustomFilter = &v3httppb.HttpFilter{
   102  			Name:       "serverOnlyOptionalCustomFilter",
   103  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   104  			IsOptional: true,
   105  		}
   106  		unknownFilter = &v3httppb.HttpFilter{
   107  			Name:       "unknownFilter",
   108  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
   109  		}
   110  		unknownOptionalFilter = &v3httppb.HttpFilter{
   111  			Name:       "unknownFilter",
   112  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
   113  			IsOptional: true,
   114  		}
   115  		v3LisWithInlineRoute = testutils.MarshalAny(t, &v3listenerpb.Listener{
   116  			Name: v3LDSTarget,
   117  			ApiListener: &v3listenerpb.ApiListener{
   118  				ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   119  					RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   120  						RouteConfig: &v3routepb.RouteConfiguration{
   121  							Name: routeName,
   122  							VirtualHosts: []*v3routepb.VirtualHost{{
   123  								Domains: []string{v3LDSTarget},
   124  								Routes: []*v3routepb.Route{{
   125  									Match: &v3routepb.RouteMatch{
   126  										PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
   127  									},
   128  									Action: &v3routepb.Route_Route{
   129  										Route: &v3routepb.RouteAction{
   130  											ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName},
   131  										}}}}}}},
   132  					},
   133  					HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
   134  					CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   135  						MaxStreamDuration: durationpb.New(time.Second),
   136  					},
   137  				}),
   138  			},
   139  		})
   140  		v3LisWithFilters = func(fs ...*v3httppb.HttpFilter) *anypb.Any {
   141  			fs = append(fs, emptyRouterFilter)
   142  			return testutils.MarshalAny(t, &v3listenerpb.Listener{
   143  				Name: v3LDSTarget,
   144  				ApiListener: &v3listenerpb.ApiListener{
   145  					ApiListener: testutils.MarshalAny(t,
   146  						&v3httppb.HttpConnectionManager{
   147  							RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   148  								Rds: &v3httppb.Rds{
   149  									ConfigSource: &v3corepb.ConfigSource{
   150  										ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   151  									},
   152  									RouteConfigName: v3RouteConfigName,
   153  								},
   154  							},
   155  							CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   156  								MaxStreamDuration: durationpb.New(time.Second),
   157  							},
   158  							HttpFilters: fs,
   159  						}),
   160  				},
   161  			})
   162  		}
   163  		v3LisToTestRBAC = func(xffNumTrustedHops uint32, originalIpDetectionExtensions []*v3corepb.TypedExtensionConfig) *anypb.Any {
   164  			return testutils.MarshalAny(t, &v3listenerpb.Listener{
   165  				Name: v3LDSTarget,
   166  				ApiListener: &v3listenerpb.ApiListener{
   167  					ApiListener: testutils.MarshalAny(t,
   168  						&v3httppb.HttpConnectionManager{
   169  							RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   170  								Rds: &v3httppb.Rds{
   171  									ConfigSource: &v3corepb.ConfigSource{
   172  										ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   173  									},
   174  									RouteConfigName: v3RouteConfigName,
   175  								},
   176  							},
   177  							CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   178  								MaxStreamDuration: durationpb.New(time.Second),
   179  							},
   180  							HttpFilters:                   []*v3httppb.HttpFilter{emptyRouterFilter},
   181  							XffNumTrustedHops:             xffNumTrustedHops,
   182  							OriginalIpDetectionExtensions: originalIpDetectionExtensions,
   183  						}),
   184  				},
   185  			})
   186  		}
   187  
   188  		v3ListenerWithCDSConfigSourceSelf = testutils.MarshalAny(t, &v3listenerpb.Listener{
   189  			Name: v3LDSTarget,
   190  			ApiListener: &v3listenerpb.ApiListener{
   191  				ApiListener: testutils.MarshalAny(t,
   192  					&v3httppb.HttpConnectionManager{
   193  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   194  							Rds: &v3httppb.Rds{
   195  								ConfigSource: &v3corepb.ConfigSource{
   196  									ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{},
   197  								},
   198  								RouteConfigName: v3RouteConfigName,
   199  							},
   200  						},
   201  						HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
   202  					}),
   203  			},
   204  		})
   205  	)
   206  
   207  	tests := []struct {
   208  		name       string
   209  		resource   *anypb.Any
   210  		wantName   string
   211  		wantUpdate ListenerUpdate
   212  		wantErr    bool
   213  	}{
   214  		{
   215  			name:     "non-listener resource",
   216  			resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL},
   217  			wantErr:  true,
   218  		},
   219  		{
   220  			name: "badly marshaled listener resource",
   221  			resource: &anypb.Any{
   222  				TypeUrl: version.V3ListenerURL,
   223  				Value: func() []byte {
   224  					lis := &v3listenerpb.Listener{
   225  						Name: v3LDSTarget,
   226  						ApiListener: &v3listenerpb.ApiListener{
   227  							ApiListener: &anypb.Any{
   228  								TypeUrl: version.V3HTTPConnManagerURL,
   229  								Value:   []byte{1, 2, 3, 4},
   230  							},
   231  						},
   232  					}
   233  					mLis, _ := proto.Marshal(lis)
   234  					return mLis
   235  				}(),
   236  			},
   237  			wantName: v3LDSTarget,
   238  			wantErr:  true,
   239  		},
   240  		{
   241  			name: "wrong type in apiListener",
   242  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   243  				Name: v3LDSTarget,
   244  				ApiListener: &v3listenerpb.ApiListener{
   245  					ApiListener: testutils.MarshalAny(t, &v2xdspb.Listener{}),
   246  				},
   247  			}),
   248  			wantName: v3LDSTarget,
   249  			wantErr:  true,
   250  		},
   251  		{
   252  			name: "empty httpConnMgr in apiListener",
   253  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   254  				Name: v3LDSTarget,
   255  				ApiListener: &v3listenerpb.ApiListener{
   256  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   257  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   258  							Rds: &v3httppb.Rds{},
   259  						},
   260  					}),
   261  				},
   262  			}),
   263  			wantName: v3LDSTarget,
   264  			wantErr:  true,
   265  		},
   266  		{
   267  			name: "scopedRoutes routeConfig in apiListener",
   268  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   269  				Name: v3LDSTarget,
   270  				ApiListener: &v3listenerpb.ApiListener{
   271  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   272  						RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
   273  					}),
   274  				},
   275  			}),
   276  			wantName: v3LDSTarget,
   277  			wantErr:  true,
   278  		},
   279  		{
   280  			name:     "rds.ConfigSource in apiListener is Self",
   281  			resource: v3ListenerWithCDSConfigSourceSelf,
   282  			wantName: v3LDSTarget,
   283  			wantUpdate: ListenerUpdate{
   284  				RouteConfigName: v3RouteConfigName,
   285  				HTTPFilters:     []HTTPFilter{makeRouterFilter(t)},
   286  				Raw:             v3ListenerWithCDSConfigSourceSelf,
   287  			},
   288  		},
   289  		{
   290  			name: "rds.ConfigSource in apiListener is not ADS or Self",
   291  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   292  				Name: v3LDSTarget,
   293  				ApiListener: &v3listenerpb.ApiListener{
   294  					ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   295  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   296  							Rds: &v3httppb.Rds{
   297  								ConfigSource: &v3corepb.ConfigSource{
   298  									ConfigSourceSpecifier: &v3corepb.ConfigSource_Path{
   299  										Path: "/some/path",
   300  									},
   301  								},
   302  								RouteConfigName: v3RouteConfigName,
   303  							},
   304  						},
   305  					}),
   306  				},
   307  			}),
   308  			wantName: v3LDSTarget,
   309  			wantErr:  true,
   310  		},
   311  		{
   312  			name:     "v3 with no filters",
   313  			resource: v3LisWithFilters(),
   314  			wantName: v3LDSTarget,
   315  			wantUpdate: ListenerUpdate{
   316  				RouteConfigName:   v3RouteConfigName,
   317  				MaxStreamDuration: time.Second,
   318  				HTTPFilters:       makeRouterFilterList(t),
   319  				Raw:               v3LisWithFilters(),
   320  			},
   321  		},
   322  		{
   323  			name: "v3 no terminal filter",
   324  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   325  				Name: v3LDSTarget,
   326  				ApiListener: &v3listenerpb.ApiListener{
   327  					ApiListener: testutils.MarshalAny(t,
   328  						&v3httppb.HttpConnectionManager{
   329  							RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   330  								Rds: &v3httppb.Rds{
   331  									ConfigSource: &v3corepb.ConfigSource{
   332  										ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   333  									},
   334  									RouteConfigName: v3RouteConfigName,
   335  								},
   336  							},
   337  							CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   338  								MaxStreamDuration: durationpb.New(time.Second),
   339  							},
   340  						}),
   341  				},
   342  			}),
   343  			wantName: v3LDSTarget,
   344  			wantErr:  true,
   345  		},
   346  		{
   347  			name:     "v3 with custom filter",
   348  			resource: v3LisWithFilters(customFilter),
   349  			wantName: v3LDSTarget,
   350  			wantUpdate: ListenerUpdate{
   351  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   352  				HTTPFilters: []HTTPFilter{
   353  					{
   354  						Name:   "customFilter",
   355  						Filter: httpFilter{},
   356  						Config: filterConfig{Cfg: customFilterConfig},
   357  					},
   358  					makeRouterFilter(t),
   359  				},
   360  				Raw: v3LisWithFilters(customFilter),
   361  			},
   362  		},
   363  		{
   364  			name:     "v3 with custom filter in old typed struct",
   365  			resource: v3LisWithFilters(oldTypedStructFilter),
   366  			wantName: v3LDSTarget,
   367  			wantUpdate: ListenerUpdate{
   368  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   369  				HTTPFilters: []HTTPFilter{
   370  					{
   371  						Name:   "customFilter",
   372  						Filter: httpFilter{},
   373  						Config: filterConfig{Cfg: customFilterOldTypedStructConfig},
   374  					},
   375  					makeRouterFilter(t),
   376  				},
   377  				Raw: v3LisWithFilters(oldTypedStructFilter),
   378  			},
   379  		},
   380  		{
   381  			name:     "v3 with custom filter in new typed struct",
   382  			resource: v3LisWithFilters(newTypedStructFilter),
   383  			wantName: v3LDSTarget,
   384  			wantUpdate: ListenerUpdate{
   385  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   386  				HTTPFilters: []HTTPFilter{
   387  					{
   388  						Name:   "customFilter",
   389  						Filter: httpFilter{},
   390  						Config: filterConfig{Cfg: customFilterNewTypedStructConfig},
   391  					},
   392  					makeRouterFilter(t),
   393  				},
   394  				Raw: v3LisWithFilters(newTypedStructFilter),
   395  			},
   396  		},
   397  		{
   398  			name:     "v3 with optional custom filter",
   399  			resource: v3LisWithFilters(customOptionalFilter),
   400  			wantName: v3LDSTarget,
   401  			wantUpdate: ListenerUpdate{
   402  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   403  				HTTPFilters: []HTTPFilter{
   404  					{
   405  						Name:   "customFilter",
   406  						Filter: httpFilter{},
   407  						Config: filterConfig{Cfg: customFilterConfig},
   408  					},
   409  					makeRouterFilter(t),
   410  				},
   411  				Raw: v3LisWithFilters(customOptionalFilter),
   412  			},
   413  		},
   414  		{
   415  			name:     "v3 with two filters with same name",
   416  			resource: v3LisWithFilters(customFilter, customFilter),
   417  			wantName: v3LDSTarget,
   418  			wantErr:  true,
   419  		},
   420  		{
   421  			name:     "v3 with two filters - same type different name",
   422  			resource: v3LisWithFilters(customFilter, customFilter2),
   423  			wantName: v3LDSTarget,
   424  			wantUpdate: ListenerUpdate{
   425  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   426  				HTTPFilters: []HTTPFilter{{
   427  					Name:   "customFilter",
   428  					Filter: httpFilter{},
   429  					Config: filterConfig{Cfg: customFilterConfig},
   430  				}, {
   431  					Name:   "customFilter2",
   432  					Filter: httpFilter{},
   433  					Config: filterConfig{Cfg: customFilterConfig},
   434  				},
   435  					makeRouterFilter(t),
   436  				},
   437  				Raw: v3LisWithFilters(customFilter, customFilter2),
   438  			},
   439  		},
   440  		{
   441  			name:     "v3 with server-only filter",
   442  			resource: v3LisWithFilters(serverOnlyCustomFilter),
   443  			wantName: v3LDSTarget,
   444  			wantErr:  true,
   445  		},
   446  		{
   447  			name:     "v3 with optional server-only filter",
   448  			resource: v3LisWithFilters(serverOnlyOptionalCustomFilter),
   449  			wantName: v3LDSTarget,
   450  			wantUpdate: ListenerUpdate{
   451  				RouteConfigName:   v3RouteConfigName,
   452  				MaxStreamDuration: time.Second,
   453  				Raw:               v3LisWithFilters(serverOnlyOptionalCustomFilter),
   454  				HTTPFilters:       makeRouterFilterList(t),
   455  			},
   456  		},
   457  		{
   458  			name:     "v3 with client-only filter",
   459  			resource: v3LisWithFilters(clientOnlyCustomFilter),
   460  			wantName: v3LDSTarget,
   461  			wantUpdate: ListenerUpdate{
   462  				RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   463  				HTTPFilters: []HTTPFilter{
   464  					{
   465  						Name:   "clientOnlyCustomFilter",
   466  						Filter: clientOnlyHTTPFilter{},
   467  						Config: filterConfig{Cfg: clientOnlyCustomFilterConfig},
   468  					},
   469  					makeRouterFilter(t)},
   470  				Raw: v3LisWithFilters(clientOnlyCustomFilter),
   471  			},
   472  		},
   473  		{
   474  			name:     "v3 with err filter",
   475  			resource: v3LisWithFilters(errFilter),
   476  			wantName: v3LDSTarget,
   477  			wantErr:  true,
   478  		},
   479  		{
   480  			name:     "v3 with optional err filter",
   481  			resource: v3LisWithFilters(errOptionalFilter),
   482  			wantName: v3LDSTarget,
   483  			wantErr:  true,
   484  		},
   485  		{
   486  			name:     "v3 with unknown filter",
   487  			resource: v3LisWithFilters(unknownFilter),
   488  			wantName: v3LDSTarget,
   489  			wantErr:  true,
   490  		},
   491  		{
   492  			name:     "v3 with unknown filter (optional)",
   493  			resource: v3LisWithFilters(unknownOptionalFilter),
   494  			wantName: v3LDSTarget,
   495  			wantUpdate: ListenerUpdate{
   496  				RouteConfigName:   v3RouteConfigName,
   497  				MaxStreamDuration: time.Second,
   498  				HTTPFilters:       makeRouterFilterList(t),
   499  				Raw:               v3LisWithFilters(unknownOptionalFilter),
   500  			},
   501  		},
   502  		{
   503  			name:     "v3 listener resource",
   504  			resource: v3LisWithFilters(),
   505  			wantName: v3LDSTarget,
   506  			wantUpdate: ListenerUpdate{
   507  				RouteConfigName:   v3RouteConfigName,
   508  				MaxStreamDuration: time.Second,
   509  				HTTPFilters:       makeRouterFilterList(t),
   510  				Raw:               v3LisWithFilters(),
   511  			},
   512  		},
   513  		{
   514  			name:     "v3 listener resource wrapped",
   515  			resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3LisWithFilters()}),
   516  			wantName: v3LDSTarget,
   517  			wantUpdate: ListenerUpdate{
   518  				RouteConfigName:   v3RouteConfigName,
   519  				MaxStreamDuration: time.Second,
   520  				HTTPFilters:       makeRouterFilterList(t),
   521  				Raw:               v3LisWithFilters(),
   522  			},
   523  		},
   524  		// "To allow equating RBAC's direct_remote_ip and
   525  		// remote_ip...HttpConnectionManager.xff_num_trusted_hops must be unset
   526  		// or zero and HttpConnectionManager.original_ip_detection_extensions
   527  		// must be empty." - A41
   528  		{
   529  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-valid",
   530  			resource: v3LisToTestRBAC(0, nil),
   531  			wantName: v3LDSTarget,
   532  			wantUpdate: ListenerUpdate{
   533  				RouteConfigName:   v3RouteConfigName,
   534  				MaxStreamDuration: time.Second,
   535  				HTTPFilters:       []HTTPFilter{makeRouterFilter(t)},
   536  				Raw:               v3LisToTestRBAC(0, nil),
   537  			},
   538  		},
   539  		// In order to support xDS Configured RBAC HTTPFilter equating direct
   540  		// remote ip and remote ip, xffNumTrustedHops cannot be greater than
   541  		// zero. This is because if you can trust a ingress proxy hop when
   542  		// determining an origin clients ip address, direct remote ip != remote
   543  		// ip.
   544  		{
   545  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-num-untrusted-hops",
   546  			resource: v3LisToTestRBAC(1, nil),
   547  			wantName: v3LDSTarget,
   548  			wantErr:  true,
   549  		},
   550  		// In order to support xDS Configured RBAC HTTPFilter equating direct
   551  		// remote ip and remote ip, originalIpDetectionExtensions must be empty.
   552  		// This is because if you have to ask ip-detection-extension for the
   553  		// original ip, direct remote ip might not equal remote ip.
   554  		{
   555  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-original-ip-detection-extension",
   556  			resource: v3LisToTestRBAC(0, []*v3corepb.TypedExtensionConfig{{Name: "something"}}),
   557  			wantName: v3LDSTarget,
   558  			wantErr:  true,
   559  		},
   560  		{
   561  			name:     "v3 listener with inline route configuration",
   562  			resource: v3LisWithInlineRoute,
   563  			wantName: v3LDSTarget,
   564  			wantUpdate: ListenerUpdate{
   565  				InlineRouteConfig: &RouteConfigUpdate{
   566  					VirtualHosts: []*VirtualHost{{
   567  						Domains: []string{v3LDSTarget},
   568  						Routes:  []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, ActionType: RouteActionRoute}},
   569  					}}},
   570  				MaxStreamDuration: time.Second,
   571  				Raw:               v3LisWithInlineRoute,
   572  				HTTPFilters:       makeRouterFilterList(t),
   573  			},
   574  		},
   575  	}
   576  
   577  	for _, test := range tests {
   578  		t.Run(test.name, func(t *testing.T) {
   579  			name, update, err := unmarshalListenerResource(test.resource)
   580  			if (err != nil) != test.wantErr {
   581  				t.Errorf("unmarshalListenerResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr)
   582  			}
   583  			if name != test.wantName {
   584  				t.Errorf("unmarshalListenerResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName)
   585  			}
   586  			if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
   587  				t.Errorf("unmarshalListenerResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff)
   588  			}
   589  		})
   590  	}
   591  }
   592  
   593  func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
   594  	const (
   595  		v3LDSTarget = "grpc/server?xds.resource.listening_address=0.0.0.0:9999"
   596  		testVersion = "test-version-lds-server"
   597  	)
   598  
   599  	var (
   600  		serverOnlyCustomFilter = &v3httppb.HttpFilter{
   601  			Name:       "serverOnlyCustomFilter",
   602  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   603  		}
   604  		routeConfig = &v3routepb.RouteConfiguration{
   605  			Name: "routeName",
   606  			VirtualHosts: []*v3routepb.VirtualHost{{
   607  				Domains: []string{"lds.target.good:3333"},
   608  				Routes: []*v3routepb.Route{{
   609  					Match: &v3routepb.RouteMatch{
   610  						PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
   611  					},
   612  					Action: &v3routepb.Route_NonForwardingAction{},
   613  				}}}}}
   614  		inlineRouteConfig = &RouteConfigUpdate{
   615  			VirtualHosts: []*VirtualHost{{
   616  				Domains: []string{"lds.target.good:3333"},
   617  				Routes:  []*Route{{Prefix: newStringP("/"), ActionType: RouteActionNonForwardingAction}},
   618  			}}}
   619  		emptyValidNetworkFilters = []*v3listenerpb.Filter{
   620  			{
   621  				Name: "filter-1",
   622  				ConfigType: &v3listenerpb.Filter_TypedConfig{
   623  					TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   624  						RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   625  							RouteConfig: routeConfig,
   626  						},
   627  						HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
   628  					}),
   629  				},
   630  			},
   631  		}
   632  		localSocketAddress = &v3corepb.Address{
   633  			Address: &v3corepb.Address_SocketAddress{
   634  				SocketAddress: &v3corepb.SocketAddress{
   635  					Address: "0.0.0.0",
   636  					PortSpecifier: &v3corepb.SocketAddress_PortValue{
   637  						PortValue: 9999,
   638  					},
   639  				},
   640  			},
   641  		}
   642  		listenerEmptyTransportSocket = testutils.MarshalAny(t, &v3listenerpb.Listener{
   643  			Name:    v3LDSTarget,
   644  			Address: localSocketAddress,
   645  			FilterChains: []*v3listenerpb.FilterChain{
   646  				{
   647  					Name:    "filter-chain-1",
   648  					Filters: emptyValidNetworkFilters,
   649  				},
   650  			},
   651  		})
   652  		listenerNoValidationContextDeprecatedFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   653  			Name:    v3LDSTarget,
   654  			Address: localSocketAddress,
   655  			FilterChains: []*v3listenerpb.FilterChain{
   656  				{
   657  					Name:    "filter-chain-1",
   658  					Filters: emptyValidNetworkFilters,
   659  					TransportSocket: &v3corepb.TransportSocket{
   660  						Name: "envoy.transport_sockets.tls",
   661  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   662  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   663  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   664  									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   665  										InstanceName:    "identityPluginInstance",
   666  										CertificateName: "identityCertName",
   667  									},
   668  								},
   669  							}),
   670  						},
   671  					},
   672  				},
   673  			},
   674  			DefaultFilterChain: &v3listenerpb.FilterChain{
   675  				Name:    "default-filter-chain-1",
   676  				Filters: emptyValidNetworkFilters,
   677  				TransportSocket: &v3corepb.TransportSocket{
   678  					Name: "envoy.transport_sockets.tls",
   679  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   680  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   681  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   682  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   683  									InstanceName:    "defaultIdentityPluginInstance",
   684  									CertificateName: "defaultIdentityCertName",
   685  								},
   686  							},
   687  						}),
   688  					},
   689  				},
   690  			},
   691  		})
   692  		listenerNoValidationContextNewFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   693  			Name:    v3LDSTarget,
   694  			Address: localSocketAddress,
   695  			FilterChains: []*v3listenerpb.FilterChain{
   696  				{
   697  					Name:    "filter-chain-1",
   698  					Filters: emptyValidNetworkFilters,
   699  					TransportSocket: &v3corepb.TransportSocket{
   700  						Name: "envoy.transport_sockets.tls",
   701  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   702  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   703  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   704  									TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   705  										InstanceName:    "identityPluginInstance",
   706  										CertificateName: "identityCertName",
   707  									},
   708  								},
   709  							}),
   710  						},
   711  					},
   712  				},
   713  			},
   714  			DefaultFilterChain: &v3listenerpb.FilterChain{
   715  				Name:    "default-filter-chain-1",
   716  				Filters: emptyValidNetworkFilters,
   717  				TransportSocket: &v3corepb.TransportSocket{
   718  					Name: "envoy.transport_sockets.tls",
   719  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   720  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   721  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   722  								TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   723  									InstanceName:    "defaultIdentityPluginInstance",
   724  									CertificateName: "defaultIdentityCertName",
   725  								},
   726  							},
   727  						}),
   728  					},
   729  				},
   730  			},
   731  		})
   732  		listenerWithValidationContextDeprecatedFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   733  			Name:    v3LDSTarget,
   734  			Address: localSocketAddress,
   735  			FilterChains: []*v3listenerpb.FilterChain{
   736  				{
   737  					Name:    "filter-chain-1",
   738  					Filters: emptyValidNetworkFilters,
   739  					TransportSocket: &v3corepb.TransportSocket{
   740  						Name: "envoy.transport_sockets.tls",
   741  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   742  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   743  								RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   744  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   745  									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   746  										InstanceName:    "identityPluginInstance",
   747  										CertificateName: "identityCertName",
   748  									},
   749  									ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   750  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   751  											InstanceName:    "rootPluginInstance",
   752  											CertificateName: "rootCertName",
   753  										},
   754  									},
   755  								},
   756  							}),
   757  						},
   758  					},
   759  				},
   760  			},
   761  			DefaultFilterChain: &v3listenerpb.FilterChain{
   762  				Name:    "default-filter-chain-1",
   763  				Filters: emptyValidNetworkFilters,
   764  				TransportSocket: &v3corepb.TransportSocket{
   765  					Name: "envoy.transport_sockets.tls",
   766  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   767  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   768  							RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   769  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   770  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   771  									InstanceName:    "defaultIdentityPluginInstance",
   772  									CertificateName: "defaultIdentityCertName",
   773  								},
   774  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   775  									ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   776  										InstanceName:    "defaultRootPluginInstance",
   777  										CertificateName: "defaultRootCertName",
   778  									},
   779  								},
   780  							},
   781  						}),
   782  					},
   783  				},
   784  			},
   785  		})
   786  		listenerWithValidationContextNewFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
   787  			Name:    v3LDSTarget,
   788  			Address: localSocketAddress,
   789  			FilterChains: []*v3listenerpb.FilterChain{
   790  				{
   791  					Name:    "filter-chain-1",
   792  					Filters: emptyValidNetworkFilters,
   793  					TransportSocket: &v3corepb.TransportSocket{
   794  						Name: "envoy.transport_sockets.tls",
   795  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   796  							TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   797  								RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   798  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   799  									TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   800  										InstanceName:    "identityPluginInstance",
   801  										CertificateName: "identityCertName",
   802  									},
   803  									ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
   804  										ValidationContext: &v3tlspb.CertificateValidationContext{
   805  											CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   806  												InstanceName:    "rootPluginInstance",
   807  												CertificateName: "rootCertName",
   808  											},
   809  										},
   810  									},
   811  								},
   812  							}),
   813  						},
   814  					},
   815  				},
   816  			},
   817  			DefaultFilterChain: &v3listenerpb.FilterChain{
   818  				Name:    "default-filter-chain-1",
   819  				Filters: emptyValidNetworkFilters,
   820  				TransportSocket: &v3corepb.TransportSocket{
   821  					Name: "envoy.transport_sockets.tls",
   822  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   823  						TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
   824  							RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   825  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   826  								TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   827  									InstanceName:    "defaultIdentityPluginInstance",
   828  									CertificateName: "defaultIdentityCertName",
   829  								},
   830  								ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
   831  									CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
   832  										DefaultValidationContext: &v3tlspb.CertificateValidationContext{
   833  											CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
   834  												InstanceName:    "defaultRootPluginInstance",
   835  												CertificateName: "defaultRootCertName",
   836  											},
   837  										},
   838  									},
   839  								},
   840  							},
   841  						}),
   842  					},
   843  				},
   844  			},
   845  		})
   846  	)
   847  	v3LisToTestRBAC := func(xffNumTrustedHops uint32, originalIpDetectionExtensions []*v3corepb.TypedExtensionConfig) *anypb.Any {
   848  		return testutils.MarshalAny(t, &v3listenerpb.Listener{
   849  			Name:    v3LDSTarget,
   850  			Address: localSocketAddress,
   851  			FilterChains: []*v3listenerpb.FilterChain{
   852  				{
   853  					Name: "filter-chain-1",
   854  					Filters: []*v3listenerpb.Filter{
   855  						{
   856  							Name: "filter-1",
   857  							ConfigType: &v3listenerpb.Filter_TypedConfig{
   858  								TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   859  									RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   860  										RouteConfig: routeConfig,
   861  									},
   862  									HttpFilters:                   []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
   863  									XffNumTrustedHops:             xffNumTrustedHops,
   864  									OriginalIpDetectionExtensions: originalIpDetectionExtensions,
   865  								}),
   866  							},
   867  						},
   868  					},
   869  				},
   870  			},
   871  		})
   872  	}
   873  	v3LisWithBadRBACConfiguration := func(rbacCfg *v3rbacpb.RBAC) *anypb.Any {
   874  		return testutils.MarshalAny(t, &v3listenerpb.Listener{
   875  			Name:    v3LDSTarget,
   876  			Address: localSocketAddress,
   877  			FilterChains: []*v3listenerpb.FilterChain{
   878  				{
   879  					Name: "filter-chain-1",
   880  					Filters: []*v3listenerpb.Filter{
   881  						{
   882  							Name: "filter-1",
   883  							ConfigType: &v3listenerpb.Filter_TypedConfig{
   884  								TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
   885  									RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   886  										RouteConfig: routeConfig,
   887  									},
   888  									HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("rbac", rbacCfg), e2e.RouterHTTPFilter},
   889  								}),
   890  							},
   891  						},
   892  					},
   893  				},
   894  			},
   895  		})
   896  	}
   897  	badRBACCfgRegex := &v3rbacpb.RBAC{
   898  		Rules: &rpb.RBAC{
   899  			Action: rpb.RBAC_ALLOW,
   900  			Policies: map[string]*rpb.Policy{
   901  				"bad-regex-value": {
   902  					Permissions: []*rpb.Permission{
   903  						{Rule: &rpb.Permission_Any{Any: true}},
   904  					},
   905  					Principals: []*rpb.Principal{
   906  						{Identifier: &rpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_SafeRegexMatch{SafeRegexMatch: &v3matcherpb.RegexMatcher{Regex: "["}}}}},
   907  					},
   908  				},
   909  			},
   910  		},
   911  	}
   912  	badRBACCfgDestIP := &v3rbacpb.RBAC{
   913  		Rules: &rpb.RBAC{
   914  			Action: rpb.RBAC_ALLOW,
   915  			Policies: map[string]*rpb.Policy{
   916  				"certain-destination-ip": {
   917  					Permissions: []*rpb.Permission{
   918  						{Rule: &rpb.Permission_DestinationIp{DestinationIp: &v3corepb.CidrRange{AddressPrefix: "not a correct address", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   919  					},
   920  					Principals: []*rpb.Principal{
   921  						{Identifier: &rpb.Principal_Any{Any: true}},
   922  					},
   923  				},
   924  			},
   925  		},
   926  	}
   927  
   928  	tests := []struct {
   929  		name       string
   930  		resource   *anypb.Any
   931  		wantName   string
   932  		wantUpdate ListenerUpdate
   933  		wantErr    string
   934  	}{
   935  		{
   936  			name: "non-empty listener filters",
   937  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   938  				Name: v3LDSTarget,
   939  				ListenerFilters: []*v3listenerpb.ListenerFilter{
   940  					{Name: "listener-filter-1"},
   941  				},
   942  			}),
   943  			wantName: v3LDSTarget,
   944  			wantErr:  "unsupported field 'listener_filters'",
   945  		},
   946  		{
   947  			name: "use_original_dst is set",
   948  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   949  				Name:           v3LDSTarget,
   950  				UseOriginalDst: &wrapperspb.BoolValue{Value: true},
   951  			}),
   952  			wantName: v3LDSTarget,
   953  			wantErr:  "unsupported field 'use_original_dst'",
   954  		},
   955  		{
   956  			name:     "no address field",
   957  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{Name: v3LDSTarget}),
   958  			wantName: v3LDSTarget,
   959  			wantErr:  "no address field in LDS response",
   960  		},
   961  		{
   962  			name: "no socket address field",
   963  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   964  				Name:    v3LDSTarget,
   965  				Address: &v3corepb.Address{},
   966  			}),
   967  			wantName: v3LDSTarget,
   968  			wantErr:  "no socket_address field in LDS response",
   969  		},
   970  		{
   971  			name: "no filter chains and no default filter chain",
   972  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   973  				Name:    v3LDSTarget,
   974  				Address: localSocketAddress,
   975  				FilterChains: []*v3listenerpb.FilterChain{
   976  					{
   977  						FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}},
   978  						Filters:          emptyValidNetworkFilters,
   979  					},
   980  				},
   981  			}),
   982  			wantName: v3LDSTarget,
   983  			wantErr:  "no supported filter chains and no default filter chain",
   984  		},
   985  		{
   986  			name: "missing http connection manager network filter",
   987  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
   988  				Name:    v3LDSTarget,
   989  				Address: localSocketAddress,
   990  				FilterChains: []*v3listenerpb.FilterChain{
   991  					{
   992  						Name: "filter-chain-1",
   993  					},
   994  				},
   995  			}),
   996  			wantName: v3LDSTarget,
   997  			wantErr:  "missing HttpConnectionManager filter",
   998  		},
   999  		{
  1000  			name: "missing filter name in http filter",
  1001  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1002  				Name:    v3LDSTarget,
  1003  				Address: localSocketAddress,
  1004  				FilterChains: []*v3listenerpb.FilterChain{
  1005  					{
  1006  						Name: "filter-chain-1",
  1007  						Filters: []*v3listenerpb.Filter{
  1008  							{
  1009  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1010  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{}),
  1011  								},
  1012  							},
  1013  						},
  1014  					},
  1015  				},
  1016  			}),
  1017  			wantName: v3LDSTarget,
  1018  			wantErr:  "missing name field in filter",
  1019  		},
  1020  		{
  1021  			name: "duplicate filter names in http filter",
  1022  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1023  				Name:    v3LDSTarget,
  1024  				Address: localSocketAddress,
  1025  				FilterChains: []*v3listenerpb.FilterChain{
  1026  					{
  1027  						Name: "filter-chain-1",
  1028  						Filters: []*v3listenerpb.Filter{
  1029  							{
  1030  								Name: "name",
  1031  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1032  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1033  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1034  											RouteConfig: routeConfig,
  1035  										},
  1036  										HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
  1037  									}),
  1038  								},
  1039  							},
  1040  							{
  1041  								Name: "name",
  1042  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1043  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1044  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1045  											RouteConfig: routeConfig,
  1046  										},
  1047  										HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
  1048  									}),
  1049  								},
  1050  							},
  1051  						},
  1052  					},
  1053  				},
  1054  			}),
  1055  			wantName: v3LDSTarget,
  1056  			wantErr:  "duplicate filter name",
  1057  		},
  1058  		{
  1059  			name: "no terminal filter",
  1060  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1061  				Name:    v3LDSTarget,
  1062  				Address: localSocketAddress,
  1063  				FilterChains: []*v3listenerpb.FilterChain{
  1064  					{
  1065  						Name: "filter-chain-1",
  1066  						Filters: []*v3listenerpb.Filter{
  1067  							{
  1068  								Name: "name",
  1069  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1070  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1071  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1072  											RouteConfig: routeConfig,
  1073  										},
  1074  									}),
  1075  								},
  1076  							},
  1077  						},
  1078  					},
  1079  				},
  1080  			}),
  1081  			wantName: v3LDSTarget,
  1082  			wantErr:  "http filters list is empty",
  1083  		},
  1084  		{
  1085  			name: "terminal filter not last",
  1086  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1087  				Name:    v3LDSTarget,
  1088  				Address: localSocketAddress,
  1089  				FilterChains: []*v3listenerpb.FilterChain{
  1090  					{
  1091  						Name: "filter-chain-1",
  1092  						Filters: []*v3listenerpb.Filter{
  1093  							{
  1094  								Name: "name",
  1095  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1096  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1097  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1098  											RouteConfig: routeConfig,
  1099  										},
  1100  										HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter, serverOnlyCustomFilter},
  1101  									}),
  1102  								},
  1103  							},
  1104  						},
  1105  					},
  1106  				},
  1107  			}),
  1108  			wantName: v3LDSTarget,
  1109  			wantErr:  "is a terminal filter but it is not last in the filter chain",
  1110  		},
  1111  		{
  1112  			name: "last not terminal filter",
  1113  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1114  				Name:    v3LDSTarget,
  1115  				Address: localSocketAddress,
  1116  				FilterChains: []*v3listenerpb.FilterChain{
  1117  					{
  1118  						Name: "filter-chain-1",
  1119  						Filters: []*v3listenerpb.Filter{
  1120  							{
  1121  								Name: "name",
  1122  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1123  									TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
  1124  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
  1125  											RouteConfig: routeConfig,
  1126  										},
  1127  										HttpFilters: []*v3httppb.HttpFilter{serverOnlyCustomFilter},
  1128  									}),
  1129  								},
  1130  							},
  1131  						},
  1132  					},
  1133  				},
  1134  			}),
  1135  			wantName: v3LDSTarget,
  1136  			wantErr:  "is not a terminal filter",
  1137  		},
  1138  		{
  1139  			name: "unsupported oneof in typed config of http filter",
  1140  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1141  				Name:    v3LDSTarget,
  1142  				Address: localSocketAddress,
  1143  				FilterChains: []*v3listenerpb.FilterChain{
  1144  					{
  1145  						Name: "filter-chain-1",
  1146  						Filters: []*v3listenerpb.Filter{
  1147  							{
  1148  								Name:       "name",
  1149  								ConfigType: &v3listenerpb.Filter_ConfigDiscovery{},
  1150  							},
  1151  						},
  1152  					},
  1153  				},
  1154  			}),
  1155  			wantName: v3LDSTarget,
  1156  			wantErr:  "unsupported config_type",
  1157  		},
  1158  		{
  1159  			name: "overlapping filter chain match criteria",
  1160  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1161  				Name:    v3LDSTarget,
  1162  				Address: localSocketAddress,
  1163  				FilterChains: []*v3listenerpb.FilterChain{
  1164  					{
  1165  						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}},
  1166  						Filters:          emptyValidNetworkFilters,
  1167  					},
  1168  					{
  1169  						FilterChainMatch: &v3listenerpb.FilterChainMatch{},
  1170  						Filters:          emptyValidNetworkFilters,
  1171  					},
  1172  					{
  1173  						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}},
  1174  						Filters:          emptyValidNetworkFilters,
  1175  					},
  1176  				},
  1177  			}),
  1178  			wantName: v3LDSTarget,
  1179  			wantErr:  "multiple filter chains with overlapping matching rules are defined",
  1180  		},
  1181  		{
  1182  			name: "unsupported network filter",
  1183  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1184  				Name:    v3LDSTarget,
  1185  				Address: localSocketAddress,
  1186  				FilterChains: []*v3listenerpb.FilterChain{
  1187  					{
  1188  						Name: "filter-chain-1",
  1189  						Filters: []*v3listenerpb.Filter{
  1190  							{
  1191  								Name: "name",
  1192  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1193  									TypedConfig: testutils.MarshalAny(t, &v3httppb.LocalReplyConfig{}),
  1194  								},
  1195  							},
  1196  						},
  1197  					},
  1198  				},
  1199  			}),
  1200  			wantName: v3LDSTarget,
  1201  			wantErr:  "unsupported network filter",
  1202  		},
  1203  		{
  1204  			name: "badly marshaled network filter",
  1205  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1206  				Name:    v3LDSTarget,
  1207  				Address: localSocketAddress,
  1208  				FilterChains: []*v3listenerpb.FilterChain{
  1209  					{
  1210  						Name: "filter-chain-1",
  1211  						Filters: []*v3listenerpb.Filter{
  1212  							{
  1213  								Name: "name",
  1214  								ConfigType: &v3listenerpb.Filter_TypedConfig{
  1215  									TypedConfig: &anypb.Any{
  1216  										TypeUrl: version.V3HTTPConnManagerURL,
  1217  										Value:   []byte{1, 2, 3, 4},
  1218  									},
  1219  								},
  1220  							},
  1221  						},
  1222  					},
  1223  				},
  1224  			}),
  1225  			wantName: v3LDSTarget,
  1226  			wantErr:  "failed unmarshalling of network filter",
  1227  		},
  1228  		{
  1229  			name: "unexpected transport socket name",
  1230  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1231  				Name:    v3LDSTarget,
  1232  				Address: localSocketAddress,
  1233  				FilterChains: []*v3listenerpb.FilterChain{
  1234  					{
  1235  						Name:    "filter-chain-1",
  1236  						Filters: emptyValidNetworkFilters,
  1237  						TransportSocket: &v3corepb.TransportSocket{
  1238  							Name: "unsupported-transport-socket-name",
  1239  						},
  1240  					},
  1241  				},
  1242  			}),
  1243  			wantName: v3LDSTarget,
  1244  			wantErr:  "transport_socket field has unexpected name",
  1245  		},
  1246  		{
  1247  			name: "unexpected transport socket typedConfig URL",
  1248  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1249  				Name:    v3LDSTarget,
  1250  				Address: localSocketAddress,
  1251  				FilterChains: []*v3listenerpb.FilterChain{
  1252  					{
  1253  						Name:    "filter-chain-1",
  1254  						Filters: emptyValidNetworkFilters,
  1255  						TransportSocket: &v3corepb.TransportSocket{
  1256  							Name: "envoy.transport_sockets.tls",
  1257  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1258  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{}),
  1259  							},
  1260  						},
  1261  					},
  1262  				},
  1263  			}),
  1264  			wantName: v3LDSTarget,
  1265  			wantErr:  "transport_socket field has unexpected typeURL",
  1266  		},
  1267  		{
  1268  			name: "badly marshaled transport socket",
  1269  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1270  				Name:    v3LDSTarget,
  1271  				Address: localSocketAddress,
  1272  				FilterChains: []*v3listenerpb.FilterChain{
  1273  					{
  1274  						Name:    "filter-chain-1",
  1275  						Filters: emptyValidNetworkFilters,
  1276  						TransportSocket: &v3corepb.TransportSocket{
  1277  							Name: "envoy.transport_sockets.tls",
  1278  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1279  								TypedConfig: &anypb.Any{
  1280  									TypeUrl: version.V3DownstreamTLSContextURL,
  1281  									Value:   []byte{1, 2, 3, 4},
  1282  								},
  1283  							},
  1284  						},
  1285  					},
  1286  				},
  1287  			}),
  1288  			wantName: v3LDSTarget,
  1289  			wantErr:  "failed to unmarshal DownstreamTlsContext in LDS response",
  1290  		},
  1291  		{
  1292  			name: "missing CommonTlsContext",
  1293  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1294  				Name:    v3LDSTarget,
  1295  				Address: localSocketAddress,
  1296  				FilterChains: []*v3listenerpb.FilterChain{
  1297  					{
  1298  						Name:    "filter-chain-1",
  1299  						Filters: emptyValidNetworkFilters,
  1300  						TransportSocket: &v3corepb.TransportSocket{
  1301  							Name: "envoy.transport_sockets.tls",
  1302  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1303  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{}),
  1304  							},
  1305  						},
  1306  					},
  1307  				},
  1308  			}),
  1309  			wantName: v3LDSTarget,
  1310  			wantErr:  "DownstreamTlsContext in LDS response does not contain a CommonTlsContext",
  1311  		},
  1312  		{
  1313  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-valid",
  1314  			resource: v3LisToTestRBAC(0, nil),
  1315  			wantName: v3LDSTarget,
  1316  			wantUpdate: ListenerUpdate{
  1317  				InboundListenerCfg: &InboundListenerConfig{
  1318  					Address: "0.0.0.0",
  1319  					Port:    "9999",
  1320  					FilterChains: &FilterChainManager{
  1321  						dstPrefixMap: map[string]*destPrefixEntry{
  1322  							unspecifiedPrefixMapKey: {
  1323  								srcTypeArr: [3]*sourcePrefixes{
  1324  									{
  1325  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1326  											unspecifiedPrefixMapKey: {
  1327  												srcPortMap: map[int]*FilterChain{
  1328  													0: {
  1329  														InlineRouteConfig: inlineRouteConfig,
  1330  														HTTPFilters:       makeRouterFilterList(t),
  1331  													},
  1332  												},
  1333  											},
  1334  										},
  1335  									},
  1336  								},
  1337  							},
  1338  						},
  1339  					},
  1340  				},
  1341  				Raw: listenerEmptyTransportSocket,
  1342  			},
  1343  		},
  1344  		{
  1345  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-num-untrusted-hops",
  1346  			resource: v3LisToTestRBAC(1, nil),
  1347  			wantName: v3LDSTarget,
  1348  			wantErr:  "xff_num_trusted_hops must be unset or zero",
  1349  		},
  1350  		{
  1351  			name:     "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-original-ip-detection-extension",
  1352  			resource: v3LisToTestRBAC(0, []*v3corepb.TypedExtensionConfig{{Name: "something"}}),
  1353  			wantName: v3LDSTarget,
  1354  			wantErr:  "original_ip_detection_extensions must be empty",
  1355  		},
  1356  		{
  1357  			name:     "rbac-with-invalid-regex",
  1358  			resource: v3LisWithBadRBACConfiguration(badRBACCfgRegex),
  1359  			wantName: v3LDSTarget,
  1360  			wantErr:  "error parsing config for filter",
  1361  		},
  1362  		{
  1363  			name:     "rbac-with-invalid-destination-ip-matcher",
  1364  			resource: v3LisWithBadRBACConfiguration(badRBACCfgDestIP),
  1365  			wantName: v3LDSTarget,
  1366  			wantErr:  "error parsing config for filter",
  1367  		},
  1368  		{
  1369  			name: "unsupported validation context in transport socket",
  1370  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1371  				Name:    v3LDSTarget,
  1372  				Address: localSocketAddress,
  1373  				FilterChains: []*v3listenerpb.FilterChain{
  1374  					{
  1375  						Name:    "filter-chain-1",
  1376  						Filters: emptyValidNetworkFilters,
  1377  						TransportSocket: &v3corepb.TransportSocket{
  1378  							Name: "envoy.transport_sockets.tls",
  1379  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1380  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1381  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1382  										ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
  1383  											ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
  1384  												Name: "foo-sds-secret",
  1385  											},
  1386  										},
  1387  									},
  1388  								}),
  1389  							},
  1390  						},
  1391  					},
  1392  				},
  1393  			}),
  1394  			wantName: v3LDSTarget,
  1395  			wantErr:  "validation context contains unexpected type",
  1396  		},
  1397  		{
  1398  			name:     "empty transport socket",
  1399  			resource: listenerEmptyTransportSocket,
  1400  			wantName: v3LDSTarget,
  1401  			wantUpdate: ListenerUpdate{
  1402  				InboundListenerCfg: &InboundListenerConfig{
  1403  					Address: "0.0.0.0",
  1404  					Port:    "9999",
  1405  					FilterChains: &FilterChainManager{
  1406  						dstPrefixMap: map[string]*destPrefixEntry{
  1407  							unspecifiedPrefixMapKey: {
  1408  								srcTypeArr: [3]*sourcePrefixes{
  1409  									{
  1410  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1411  											unspecifiedPrefixMapKey: {
  1412  												srcPortMap: map[int]*FilterChain{
  1413  													0: {
  1414  														InlineRouteConfig: inlineRouteConfig,
  1415  														HTTPFilters:       makeRouterFilterList(t),
  1416  													},
  1417  												},
  1418  											},
  1419  										},
  1420  									},
  1421  								},
  1422  							},
  1423  						},
  1424  					},
  1425  				},
  1426  				Raw: listenerEmptyTransportSocket,
  1427  			},
  1428  		},
  1429  		{
  1430  			name: "no identity and root certificate providers using deprecated fields",
  1431  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1432  				Name:    v3LDSTarget,
  1433  				Address: localSocketAddress,
  1434  				FilterChains: []*v3listenerpb.FilterChain{
  1435  					{
  1436  						Name:    "filter-chain-1",
  1437  						Filters: emptyValidNetworkFilters,
  1438  						TransportSocket: &v3corepb.TransportSocket{
  1439  							Name: "envoy.transport_sockets.tls",
  1440  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1441  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1442  									RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
  1443  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1444  										TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
  1445  											InstanceName:    "identityPluginInstance",
  1446  											CertificateName: "identityCertName",
  1447  										},
  1448  									},
  1449  								}),
  1450  							},
  1451  						},
  1452  					},
  1453  				},
  1454  			}),
  1455  			wantName: v3LDSTarget,
  1456  			wantErr:  "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
  1457  		},
  1458  		{
  1459  			name: "no identity and root certificate providers using new fields",
  1460  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1461  				Name:    v3LDSTarget,
  1462  				Address: localSocketAddress,
  1463  				FilterChains: []*v3listenerpb.FilterChain{
  1464  					{
  1465  						Name:    "filter-chain-1",
  1466  						Filters: emptyValidNetworkFilters,
  1467  						TransportSocket: &v3corepb.TransportSocket{
  1468  							Name: "envoy.transport_sockets.tls",
  1469  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1470  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1471  									RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
  1472  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1473  										TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
  1474  											InstanceName:    "identityPluginInstance",
  1475  											CertificateName: "identityCertName",
  1476  										},
  1477  									},
  1478  								}),
  1479  							},
  1480  						},
  1481  					},
  1482  				},
  1483  			}),
  1484  			wantName: v3LDSTarget,
  1485  			wantErr:  "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
  1486  		},
  1487  		{
  1488  			name: "no identity certificate provider with require_client_cert",
  1489  			resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
  1490  				Name:    v3LDSTarget,
  1491  				Address: localSocketAddress,
  1492  				FilterChains: []*v3listenerpb.FilterChain{
  1493  					{
  1494  						Name:    "filter-chain-1",
  1495  						Filters: emptyValidNetworkFilters,
  1496  						TransportSocket: &v3corepb.TransportSocket{
  1497  							Name: "envoy.transport_sockets.tls",
  1498  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1499  								TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
  1500  									CommonTlsContext: &v3tlspb.CommonTlsContext{},
  1501  								}),
  1502  							},
  1503  						},
  1504  					},
  1505  				},
  1506  			}),
  1507  			wantName: v3LDSTarget,
  1508  			wantErr:  "security configuration on the server-side does not contain identity certificate provider instance name",
  1509  		},
  1510  		{
  1511  			name:     "happy case with no validation context using deprecated fields",
  1512  			resource: listenerNoValidationContextDeprecatedFields,
  1513  			wantName: v3LDSTarget,
  1514  			wantUpdate: ListenerUpdate{
  1515  				InboundListenerCfg: &InboundListenerConfig{
  1516  					Address: "0.0.0.0",
  1517  					Port:    "9999",
  1518  					FilterChains: &FilterChainManager{
  1519  						dstPrefixMap: map[string]*destPrefixEntry{
  1520  							unspecifiedPrefixMapKey: {
  1521  								srcTypeArr: [3]*sourcePrefixes{
  1522  									{
  1523  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1524  											unspecifiedPrefixMapKey: {
  1525  												srcPortMap: map[int]*FilterChain{
  1526  													0: {
  1527  														SecurityCfg: &SecurityConfig{
  1528  															IdentityInstanceName: "identityPluginInstance",
  1529  															IdentityCertName:     "identityCertName",
  1530  														},
  1531  														InlineRouteConfig: inlineRouteConfig,
  1532  														HTTPFilters:       makeRouterFilterList(t),
  1533  													},
  1534  												},
  1535  											},
  1536  										},
  1537  									},
  1538  								},
  1539  							},
  1540  						},
  1541  						def: &FilterChain{
  1542  							SecurityCfg: &SecurityConfig{
  1543  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1544  								IdentityCertName:     "defaultIdentityCertName",
  1545  							},
  1546  							InlineRouteConfig: inlineRouteConfig,
  1547  							HTTPFilters:       makeRouterFilterList(t),
  1548  						},
  1549  					},
  1550  				},
  1551  				Raw: listenerNoValidationContextDeprecatedFields,
  1552  			},
  1553  		},
  1554  		{
  1555  			name:     "happy case with no validation context using new fields",
  1556  			resource: listenerNoValidationContextNewFields,
  1557  			wantName: v3LDSTarget,
  1558  			wantUpdate: ListenerUpdate{
  1559  				InboundListenerCfg: &InboundListenerConfig{
  1560  					Address: "0.0.0.0",
  1561  					Port:    "9999",
  1562  					FilterChains: &FilterChainManager{
  1563  						dstPrefixMap: map[string]*destPrefixEntry{
  1564  							unspecifiedPrefixMapKey: {
  1565  								srcTypeArr: [3]*sourcePrefixes{
  1566  									{
  1567  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1568  											unspecifiedPrefixMapKey: {
  1569  												srcPortMap: map[int]*FilterChain{
  1570  													0: {
  1571  														SecurityCfg: &SecurityConfig{
  1572  															IdentityInstanceName: "identityPluginInstance",
  1573  															IdentityCertName:     "identityCertName",
  1574  														},
  1575  														InlineRouteConfig: inlineRouteConfig,
  1576  														HTTPFilters:       makeRouterFilterList(t),
  1577  													},
  1578  												},
  1579  											},
  1580  										},
  1581  									},
  1582  								},
  1583  							},
  1584  						},
  1585  						def: &FilterChain{
  1586  							SecurityCfg: &SecurityConfig{
  1587  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1588  								IdentityCertName:     "defaultIdentityCertName",
  1589  							},
  1590  							InlineRouteConfig: inlineRouteConfig,
  1591  							HTTPFilters:       makeRouterFilterList(t),
  1592  						},
  1593  					},
  1594  				},
  1595  				Raw: listenerNoValidationContextNewFields,
  1596  			},
  1597  		},
  1598  		{
  1599  			name:     "happy case with validation context provider instance with deprecated fields",
  1600  			resource: listenerWithValidationContextDeprecatedFields,
  1601  			wantName: v3LDSTarget,
  1602  			wantUpdate: ListenerUpdate{
  1603  				InboundListenerCfg: &InboundListenerConfig{
  1604  					Address: "0.0.0.0",
  1605  					Port:    "9999",
  1606  					FilterChains: &FilterChainManager{
  1607  						dstPrefixMap: map[string]*destPrefixEntry{
  1608  							unspecifiedPrefixMapKey: {
  1609  								srcTypeArr: [3]*sourcePrefixes{
  1610  									{
  1611  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1612  											unspecifiedPrefixMapKey: {
  1613  												srcPortMap: map[int]*FilterChain{
  1614  													0: {
  1615  														SecurityCfg: &SecurityConfig{
  1616  															RootInstanceName:     "rootPluginInstance",
  1617  															RootCertName:         "rootCertName",
  1618  															IdentityInstanceName: "identityPluginInstance",
  1619  															IdentityCertName:     "identityCertName",
  1620  															RequireClientCert:    true,
  1621  														},
  1622  														InlineRouteConfig: inlineRouteConfig,
  1623  														HTTPFilters:       makeRouterFilterList(t),
  1624  													},
  1625  												},
  1626  											},
  1627  										},
  1628  									},
  1629  								},
  1630  							},
  1631  						},
  1632  						def: &FilterChain{
  1633  							SecurityCfg: &SecurityConfig{
  1634  								RootInstanceName:     "defaultRootPluginInstance",
  1635  								RootCertName:         "defaultRootCertName",
  1636  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1637  								IdentityCertName:     "defaultIdentityCertName",
  1638  								RequireClientCert:    true,
  1639  							},
  1640  							InlineRouteConfig: inlineRouteConfig,
  1641  							HTTPFilters:       makeRouterFilterList(t),
  1642  						},
  1643  					},
  1644  				},
  1645  				Raw: listenerWithValidationContextDeprecatedFields,
  1646  			},
  1647  		},
  1648  		{
  1649  			name:     "happy case with validation context provider instance with new fields",
  1650  			resource: listenerWithValidationContextNewFields,
  1651  			wantName: v3LDSTarget,
  1652  			wantUpdate: ListenerUpdate{
  1653  				InboundListenerCfg: &InboundListenerConfig{
  1654  					Address: "0.0.0.0",
  1655  					Port:    "9999",
  1656  					FilterChains: &FilterChainManager{
  1657  						dstPrefixMap: map[string]*destPrefixEntry{
  1658  							unspecifiedPrefixMapKey: {
  1659  								srcTypeArr: [3]*sourcePrefixes{
  1660  									{
  1661  										srcPrefixMap: map[string]*sourcePrefixEntry{
  1662  											unspecifiedPrefixMapKey: {
  1663  												srcPortMap: map[int]*FilterChain{
  1664  													0: {
  1665  														SecurityCfg: &SecurityConfig{
  1666  															RootInstanceName:     "rootPluginInstance",
  1667  															RootCertName:         "rootCertName",
  1668  															IdentityInstanceName: "identityPluginInstance",
  1669  															IdentityCertName:     "identityCertName",
  1670  															RequireClientCert:    true,
  1671  														},
  1672  														InlineRouteConfig: inlineRouteConfig,
  1673  														HTTPFilters:       makeRouterFilterList(t),
  1674  													},
  1675  												},
  1676  											},
  1677  										},
  1678  									},
  1679  								},
  1680  							},
  1681  						},
  1682  						def: &FilterChain{
  1683  							SecurityCfg: &SecurityConfig{
  1684  								RootInstanceName:     "defaultRootPluginInstance",
  1685  								RootCertName:         "defaultRootCertName",
  1686  								IdentityInstanceName: "defaultIdentityPluginInstance",
  1687  								IdentityCertName:     "defaultIdentityCertName",
  1688  								RequireClientCert:    true,
  1689  							},
  1690  							InlineRouteConfig: inlineRouteConfig,
  1691  							HTTPFilters:       makeRouterFilterList(t),
  1692  						},
  1693  					},
  1694  				},
  1695  				Raw: listenerWithValidationContextNewFields,
  1696  			},
  1697  		},
  1698  	}
  1699  
  1700  	for _, test := range tests {
  1701  		t.Run(test.name, func(t *testing.T) {
  1702  			name, update, err := unmarshalListenerResource(test.resource)
  1703  			if err != nil && !strings.Contains(err.Error(), test.wantErr) {
  1704  				t.Errorf("unmarshalListenerResource(%s) = %v wantErr: %q", pretty.ToJSON(test.resource), err, test.wantErr)
  1705  			}
  1706  			if name != test.wantName {
  1707  				t.Errorf("unmarshalListenerResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName)
  1708  			}
  1709  			if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
  1710  				t.Errorf("unmarshalListenerResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff)
  1711  			}
  1712  		})
  1713  	}
  1714  }
  1715  
  1716  type filterConfig struct {
  1717  	httpfilter.FilterConfig
  1718  	Cfg      proto.Message
  1719  	Override proto.Message
  1720  }
  1721  
  1722  // httpFilter allows testing the http filter registry and parsing functionality.
  1723  type httpFilter struct {
  1724  	httpfilter.ClientInterceptorBuilder
  1725  	httpfilter.ServerInterceptorBuilder
  1726  }
  1727  
  1728  func (httpFilter) TypeURLs() []string { return []string{"custom.filter"} }
  1729  
  1730  func (httpFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1731  	return filterConfig{Cfg: cfg}, nil
  1732  }
  1733  
  1734  func (httpFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1735  	return filterConfig{Override: override}, nil
  1736  }
  1737  
  1738  func (httpFilter) IsTerminal() bool {
  1739  	return false
  1740  }
  1741  
  1742  // errHTTPFilter returns errors no matter what is passed to ParseFilterConfig.
  1743  type errHTTPFilter struct {
  1744  	httpfilter.ClientInterceptorBuilder
  1745  }
  1746  
  1747  func (errHTTPFilter) TypeURLs() []string { return []string{"err.custom.filter"} }
  1748  
  1749  func (errHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1750  	return nil, fmt.Errorf("error from ParseFilterConfig")
  1751  }
  1752  
  1753  func (errHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1754  	return nil, fmt.Errorf("error from ParseFilterConfigOverride")
  1755  }
  1756  
  1757  func (errHTTPFilter) IsTerminal() bool {
  1758  	return false
  1759  }
  1760  
  1761  func init() {
  1762  	httpfilter.Register(httpFilter{})
  1763  	httpfilter.Register(errHTTPFilter{})
  1764  	httpfilter.Register(serverOnlyHTTPFilter{})
  1765  	httpfilter.Register(clientOnlyHTTPFilter{})
  1766  }
  1767  
  1768  // serverOnlyHTTPFilter does not implement ClientInterceptorBuilder
  1769  type serverOnlyHTTPFilter struct {
  1770  	httpfilter.ServerInterceptorBuilder
  1771  }
  1772  
  1773  func (serverOnlyHTTPFilter) TypeURLs() []string { return []string{"serverOnly.custom.filter"} }
  1774  
  1775  func (serverOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1776  	return filterConfig{Cfg: cfg}, nil
  1777  }
  1778  
  1779  func (serverOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1780  	return filterConfig{Override: override}, nil
  1781  }
  1782  
  1783  func (serverOnlyHTTPFilter) IsTerminal() bool {
  1784  	return false
  1785  }
  1786  
  1787  // clientOnlyHTTPFilter does not implement ServerInterceptorBuilder
  1788  type clientOnlyHTTPFilter struct {
  1789  	httpfilter.ClientInterceptorBuilder
  1790  }
  1791  
  1792  func (clientOnlyHTTPFilter) TypeURLs() []string { return []string{"clientOnly.custom.filter"} }
  1793  
  1794  func (clientOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1795  	return filterConfig{Cfg: cfg}, nil
  1796  }
  1797  
  1798  func (clientOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1799  	return filterConfig{Override: override}, nil
  1800  }
  1801  
  1802  func (clientOnlyHTTPFilter) IsTerminal() bool {
  1803  	return false
  1804  }
  1805  
  1806  var customFilterConfig = &anypb.Any{
  1807  	TypeUrl: "custom.filter",
  1808  	Value:   []byte{1, 2, 3},
  1809  }
  1810  
  1811  var errFilterConfig = &anypb.Any{
  1812  	TypeUrl: "err.custom.filter",
  1813  	Value:   []byte{1, 2, 3},
  1814  }
  1815  
  1816  var serverOnlyCustomFilterConfig = &anypb.Any{
  1817  	TypeUrl: "serverOnly.custom.filter",
  1818  	Value:   []byte{1, 2, 3},
  1819  }
  1820  
  1821  var clientOnlyCustomFilterConfig = &anypb.Any{
  1822  	TypeUrl: "clientOnly.custom.filter",
  1823  	Value:   []byte{1, 2, 3},
  1824  }
  1825  
  1826  // This custom filter uses the old TypedStruct message from the cncf/udpa repo.
  1827  var customFilterOldTypedStructConfig = &v1xdsudpatypepb.TypedStruct{
  1828  	TypeUrl: "custom.filter",
  1829  	Value: &structpb.Struct{
  1830  		Fields: map[string]*structpb.Value{
  1831  			"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1832  		},
  1833  	},
  1834  }
  1835  
  1836  // This custom filter uses the new TypedStruct message from the cncf/xds repo.
  1837  var customFilterNewTypedStructConfig = &v3xdsxdstypepb.TypedStruct{
  1838  	TypeUrl: "custom.filter",
  1839  	Value: &structpb.Struct{
  1840  		Fields: map[string]*structpb.Value{
  1841  			"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1842  		},
  1843  	},
  1844  }
  1845  
  1846  var unknownFilterConfig = &anypb.Any{
  1847  	TypeUrl: "unknown.custom.filter",
  1848  	Value:   []byte{1, 2, 3},
  1849  }
  1850  
  1851  func wrappedOptionalFilter(t *testing.T, name string) *anypb.Any {
  1852  	return testutils.MarshalAny(t, &v3routepb.FilterConfig{
  1853  		IsOptional: true,
  1854  		Config: &anypb.Any{
  1855  			TypeUrl: name,
  1856  			Value:   []byte{1, 2, 3},
  1857  		},
  1858  	})
  1859  }
  1860  

View as plain text