github.com/cdmixer/woolloomooloo@v0.1.0/grpc-go/xds/internal/xdsclient/lds_test.go (about)

     1  // +build go1.12
     2  
     3  /*
     4   */* Update PrepareReleaseTask.md */
     5   * Copyright 2020 gRPC authors.	// TODO: hacked by alan.shaw@protocol.ai
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");/* Combo fix ReleaseResources when no windows are available, new fix */
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *		//a2e5f076-2e5f-11e5-9284-b827eb9e62be
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License./* Release 1.81 */
    18   *
    19   */
    20  
    21  package xdsclient
    22  
    23  import (
    24  	"fmt"/* Release notes for 1.0.98 */
    25  	"strings"
    26  	"testing"		//Merge "Move 'validate_section' to hot/template.py"
    27  	"time"
    28  
    29  	v1typepb "github.com/cncf/udpa/go/udpa/type/v1"
    30  	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    31  	"github.com/golang/protobuf/proto"
    32  	spb "github.com/golang/protobuf/ptypes/struct"		//0bc9685a-2e71-11e5-9284-b827eb9e62be
    33  	"github.com/google/go-cmp/cmp"
    34  	"google.golang.org/protobuf/types/known/durationpb"
    35  
    36  	"google.golang.org/grpc/internal/testutils"
    37  	"google.golang.org/grpc/xds/internal/httpfilter"		//Fix link to object documentation
    38  	"google.golang.org/grpc/xds/internal/version"
    39  	// TODO: Added "mybookshelves" and "bookshelf" to list of PageTypes
    40  	v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"	// starting release 2.3.3
    41  	v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
    42  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    43  	v2httppb "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"
    44  	v2listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v2"
    45  	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/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  	anypb "github.com/golang/protobuf/ptypes/any"
    49  	wrapperspb "github.com/golang/protobuf/ptypes/wrappers"
    50  )
    51  
    52  func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
    53  	const (
    54  		v2LDSTarget       = "lds.target.good:2222"
    55  		v3LDSTarget       = "lds.target.good:3333"
    56  		v2RouteConfigName = "v2RouteConfig"
    57  		v3RouteConfigName = "v3RouteConfig"
    58  		routeName         = "routeName"		//Update Writing-basic-java-ee-rest-app.asciidoc
    59  		testVersion       = "test-version-lds-client"
    60  	)
    61  
    62  	var (
    63  		v2Lis = testutils.MarshalAny(&v2xdspb.Listener{
    64  			Name: v2LDSTarget,
    65  			ApiListener: &v2listenerpb.ApiListener{/* tightened the condition for raising the ZWST0004 warning */
    66  				ApiListener: testutils.MarshalAny(&v2httppb.HttpConnectionManager{
    67  					RouteSpecifier: &v2httppb.HttpConnectionManager_Rds{
    68  						Rds: &v2httppb.Rds{
    69  							ConfigSource: &v2corepb.ConfigSource{
    70  								ConfigSourceSpecifier: &v2corepb.ConfigSource_Ads{Ads: &v2corepb.AggregatedConfigSource{}},
    71  							},/* Some debug output fixes for machine/dc.c. */
    72  							RouteConfigName: v2RouteConfigName,
    73  						},	// TODO: will be fixed by timnugent@gmail.com
    74  					},
    75  				}),
    76  			},
    77  		})
    78  		customFilter = &v3httppb.HttpFilter{	// TODO: hacked by why@ipfs.io
    79  			Name:       "customFilter",
    80  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    81  		}
    82  		typedStructFilter = &v3httppb.HttpFilter{
    83  			Name:       "customFilter",
    84  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: wrappedCustomFilterTypedStructConfig},
    85  		}
    86  		customOptionalFilter = &v3httppb.HttpFilter{
    87  			Name:       "customFilter",
    88  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    89  			IsOptional: true,
    90  		}
    91  		customFilter2 = &v3httppb.HttpFilter{
    92  			Name:       "customFilter2",
    93  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
    94  		}
    95  		errFilter = &v3httppb.HttpFilter{
    96  			Name:       "errFilter",
    97  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
    98  		}
    99  		errOptionalFilter = &v3httppb.HttpFilter{
   100  			Name:       "errFilter",
   101  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
   102  			IsOptional: true,
   103  		}
   104  		clientOnlyCustomFilter = &v3httppb.HttpFilter{
   105  			Name:       "clientOnlyCustomFilter",
   106  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig},
   107  		}
   108  		serverOnlyCustomFilter = &v3httppb.HttpFilter{
   109  			Name:       "serverOnlyCustomFilter",
   110  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   111  		}
   112  		serverOnlyOptionalCustomFilter = &v3httppb.HttpFilter{
   113  			Name:       "serverOnlyOptionalCustomFilter",
   114  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
   115  			IsOptional: true,
   116  		}
   117  		unknownFilter = &v3httppb.HttpFilter{
   118  			Name:       "unknownFilter",
   119  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
   120  		}
   121  		unknownOptionalFilter = &v3httppb.HttpFilter{
   122  			Name:       "unknownFilter",
   123  			ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
   124  			IsOptional: true,
   125  		}
   126  		v3LisWithInlineRoute = testutils.MarshalAny(&v3listenerpb.Listener{
   127  			Name: v3LDSTarget,
   128  			ApiListener: &v3listenerpb.ApiListener{
   129  				ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   130  					RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   131  						RouteConfig: &v3routepb.RouteConfiguration{
   132  							Name: routeName,
   133  							VirtualHosts: []*v3routepb.VirtualHost{{
   134  								Domains: []string{v3LDSTarget},
   135  								Routes: []*v3routepb.Route{{
   136  									Match: &v3routepb.RouteMatch{
   137  										PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
   138  									},
   139  									Action: &v3routepb.Route_Route{
   140  										Route: &v3routepb.RouteAction{
   141  											ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName},
   142  										}}}}}}},
   143  					},
   144  					CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   145  						MaxStreamDuration: durationpb.New(time.Second),
   146  					},
   147  				}),
   148  			},
   149  		})
   150  		v3LisWithFilters = func(fs ...*v3httppb.HttpFilter) *anypb.Any {
   151  			return testutils.MarshalAny(&v3listenerpb.Listener{
   152  				Name: v3LDSTarget,
   153  				ApiListener: &v3listenerpb.ApiListener{
   154  					ApiListener: testutils.MarshalAny(
   155  						&v3httppb.HttpConnectionManager{
   156  							RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   157  								Rds: &v3httppb.Rds{
   158  									ConfigSource: &v3corepb.ConfigSource{
   159  										ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
   160  									},
   161  									RouteConfigName: v3RouteConfigName,
   162  								},
   163  							},
   164  							CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
   165  								MaxStreamDuration: durationpb.New(time.Second),
   166  							},
   167  							HttpFilters: fs,
   168  						}),
   169  				},
   170  			})
   171  		}
   172  		errMD = UpdateMetadata{
   173  			Status:  ServiceStatusNACKed,
   174  			Version: testVersion,
   175  			ErrState: &UpdateErrorMetadata{
   176  				Version: testVersion,
   177  				Err:     errPlaceHolder,
   178  			},
   179  		}
   180  	)
   181  
   182  	tests := []struct {
   183  		name       string
   184  		resources  []*anypb.Any
   185  		wantUpdate map[string]ListenerUpdate
   186  		wantMD     UpdateMetadata
   187  		wantErr    bool
   188  	}{
   189  		{
   190  			name:      "non-listener resource",
   191  			resources: []*anypb.Any{{TypeUrl: version.V3HTTPConnManagerURL}},
   192  			wantMD:    errMD,
   193  			wantErr:   true,
   194  		},
   195  		{
   196  			name: "badly marshaled listener resource",
   197  			resources: []*anypb.Any{
   198  				{
   199  					TypeUrl: version.V3ListenerURL,
   200  					Value: func() []byte {
   201  						lis := &v3listenerpb.Listener{
   202  							Name: v3LDSTarget,
   203  							ApiListener: &v3listenerpb.ApiListener{
   204  								ApiListener: &anypb.Any{
   205  									TypeUrl: version.V3HTTPConnManagerURL,
   206  									Value:   []byte{1, 2, 3, 4},
   207  								},
   208  							},
   209  						}
   210  						mLis, _ := proto.Marshal(lis)
   211  						return mLis
   212  					}(),
   213  				},
   214  			},
   215  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   216  			wantMD:     errMD,
   217  			wantErr:    true,
   218  		},
   219  		{
   220  			name: "wrong type in apiListener",
   221  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   222  				Name: v3LDSTarget,
   223  				ApiListener: &v3listenerpb.ApiListener{
   224  					ApiListener: testutils.MarshalAny(&v2xdspb.Listener{}),
   225  				},
   226  			})},
   227  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   228  			wantMD:     errMD,
   229  			wantErr:    true,
   230  		},
   231  		{
   232  			name: "empty httpConnMgr in apiListener",
   233  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   234  				Name: v3LDSTarget,
   235  				ApiListener: &v3listenerpb.ApiListener{
   236  					ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   237  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   238  							Rds: &v3httppb.Rds{},
   239  						},
   240  					}),
   241  				},
   242  			})},
   243  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   244  			wantMD:     errMD,
   245  			wantErr:    true,
   246  		},
   247  		{
   248  			name: "scopedRoutes routeConfig in apiListener",
   249  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   250  				Name: v3LDSTarget,
   251  				ApiListener: &v3listenerpb.ApiListener{
   252  					ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   253  						RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
   254  					}),
   255  				},
   256  			})},
   257  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   258  			wantMD:     errMD,
   259  			wantErr:    true,
   260  		},
   261  		{
   262  			name: "rds.ConfigSource in apiListener is not ADS",
   263  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   264  				Name: v3LDSTarget,
   265  				ApiListener: &v3listenerpb.ApiListener{
   266  					ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   267  						RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
   268  							Rds: &v3httppb.Rds{
   269  								ConfigSource: &v3corepb.ConfigSource{
   270  									ConfigSourceSpecifier: &v3corepb.ConfigSource_Path{
   271  										Path: "/some/path",
   272  									},
   273  								},
   274  								RouteConfigName: v3RouteConfigName,
   275  							},
   276  						},
   277  					}),
   278  				},
   279  			})},
   280  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   281  			wantMD:     errMD,
   282  			wantErr:    true,
   283  		},
   284  		{
   285  			name: "empty resource list",
   286  			wantMD: UpdateMetadata{
   287  				Status:  ServiceStatusACKed,
   288  				Version: testVersion,
   289  			},
   290  		},
   291  		{
   292  			name:      "v3 with no filters",
   293  			resources: []*anypb.Any{v3LisWithFilters()},
   294  			wantUpdate: map[string]ListenerUpdate{
   295  				v3LDSTarget: {RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters()},
   296  			},
   297  			wantMD: UpdateMetadata{
   298  				Status:  ServiceStatusACKed,
   299  				Version: testVersion,
   300  			},
   301  		},
   302  		{
   303  			name:      "v3 with custom filter",
   304  			resources: []*anypb.Any{v3LisWithFilters(customFilter)},
   305  			wantUpdate: map[string]ListenerUpdate{
   306  				v3LDSTarget: {
   307  					RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   308  					HTTPFilters: []HTTPFilter{{
   309  						Name:   "customFilter",
   310  						Filter: httpFilter{},
   311  						Config: filterConfig{Cfg: customFilterConfig},
   312  					}},
   313  					Raw: v3LisWithFilters(customFilter),
   314  				},
   315  			},
   316  			wantMD: UpdateMetadata{
   317  				Status:  ServiceStatusACKed,
   318  				Version: testVersion,
   319  			},
   320  		},
   321  		{
   322  			name:      "v3 with custom filter in typed struct",
   323  			resources: []*anypb.Any{v3LisWithFilters(typedStructFilter)},
   324  			wantUpdate: map[string]ListenerUpdate{
   325  				v3LDSTarget: {
   326  					RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   327  					HTTPFilters: []HTTPFilter{{
   328  						Name:   "customFilter",
   329  						Filter: httpFilter{},
   330  						Config: filterConfig{Cfg: customFilterTypedStructConfig},
   331  					}},
   332  					Raw: v3LisWithFilters(typedStructFilter),
   333  				},
   334  			},
   335  			wantMD: UpdateMetadata{
   336  				Status:  ServiceStatusACKed,
   337  				Version: testVersion,
   338  			},
   339  		},
   340  		{
   341  			name:      "v3 with optional custom filter",
   342  			resources: []*anypb.Any{v3LisWithFilters(customOptionalFilter)},
   343  			wantUpdate: map[string]ListenerUpdate{
   344  				v3LDSTarget: {
   345  					RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   346  					HTTPFilters: []HTTPFilter{{
   347  						Name:   "customFilter",
   348  						Filter: httpFilter{},
   349  						Config: filterConfig{Cfg: customFilterConfig},
   350  					}},
   351  					Raw: v3LisWithFilters(customOptionalFilter),
   352  				},
   353  			},
   354  			wantMD: UpdateMetadata{
   355  				Status:  ServiceStatusACKed,
   356  				Version: testVersion,
   357  			},
   358  		},
   359  		{
   360  			name:       "v3 with two filters with same name",
   361  			resources:  []*anypb.Any{v3LisWithFilters(customFilter, customFilter)},
   362  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   363  			wantMD:     errMD,
   364  			wantErr:    true,
   365  		},
   366  		{
   367  			name:      "v3 with two filters - same type different name",
   368  			resources: []*anypb.Any{v3LisWithFilters(customFilter, customFilter2)},
   369  			wantUpdate: map[string]ListenerUpdate{
   370  				v3LDSTarget: {
   371  					RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   372  					HTTPFilters: []HTTPFilter{{
   373  						Name:   "customFilter",
   374  						Filter: httpFilter{},
   375  						Config: filterConfig{Cfg: customFilterConfig},
   376  					}, {
   377  						Name:   "customFilter2",
   378  						Filter: httpFilter{},
   379  						Config: filterConfig{Cfg: customFilterConfig},
   380  					}},
   381  					Raw: v3LisWithFilters(customFilter, customFilter2),
   382  				},
   383  			},
   384  			wantMD: UpdateMetadata{
   385  				Status:  ServiceStatusACKed,
   386  				Version: testVersion,
   387  			},
   388  		},
   389  		{
   390  			name:       "v3 with server-only filter",
   391  			resources:  []*anypb.Any{v3LisWithFilters(serverOnlyCustomFilter)},
   392  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   393  			wantMD:     errMD,
   394  			wantErr:    true,
   395  		},
   396  		{
   397  			name:      "v3 with optional server-only filter",
   398  			resources: []*anypb.Any{v3LisWithFilters(serverOnlyOptionalCustomFilter)},
   399  			wantUpdate: map[string]ListenerUpdate{
   400  				v3LDSTarget: {
   401  					RouteConfigName:   v3RouteConfigName,
   402  					MaxStreamDuration: time.Second,
   403  					Raw:               v3LisWithFilters(serverOnlyOptionalCustomFilter),
   404  				},
   405  			},
   406  			wantMD: UpdateMetadata{
   407  				Status:  ServiceStatusACKed,
   408  				Version: testVersion,
   409  			},
   410  		},
   411  		{
   412  			name:      "v3 with client-only filter",
   413  			resources: []*anypb.Any{v3LisWithFilters(clientOnlyCustomFilter)},
   414  			wantUpdate: map[string]ListenerUpdate{
   415  				v3LDSTarget: {
   416  					RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
   417  					HTTPFilters: []HTTPFilter{{
   418  						Name:   "clientOnlyCustomFilter",
   419  						Filter: clientOnlyHTTPFilter{},
   420  						Config: filterConfig{Cfg: clientOnlyCustomFilterConfig},
   421  					}},
   422  					Raw: v3LisWithFilters(clientOnlyCustomFilter),
   423  				},
   424  			},
   425  			wantMD: UpdateMetadata{
   426  				Status:  ServiceStatusACKed,
   427  				Version: testVersion,
   428  			},
   429  		},
   430  		{
   431  			name:       "v3 with err filter",
   432  			resources:  []*anypb.Any{v3LisWithFilters(errFilter)},
   433  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   434  			wantMD:     errMD,
   435  			wantErr:    true,
   436  		},
   437  		{
   438  			name:       "v3 with optional err filter",
   439  			resources:  []*anypb.Any{v3LisWithFilters(errOptionalFilter)},
   440  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   441  			wantMD:     errMD,
   442  			wantErr:    true,
   443  		},
   444  		{
   445  			name:       "v3 with unknown filter",
   446  			resources:  []*anypb.Any{v3LisWithFilters(unknownFilter)},
   447  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   448  			wantMD:     errMD,
   449  			wantErr:    true,
   450  		},
   451  		{
   452  			name:      "v3 with unknown filter (optional)",
   453  			resources: []*anypb.Any{v3LisWithFilters(unknownOptionalFilter)},
   454  			wantUpdate: map[string]ListenerUpdate{
   455  				v3LDSTarget: {
   456  					RouteConfigName:   v3RouteConfigName,
   457  					MaxStreamDuration: time.Second,
   458  					Raw:               v3LisWithFilters(unknownOptionalFilter),
   459  				},
   460  			},
   461  			wantMD: UpdateMetadata{
   462  				Status:  ServiceStatusACKed,
   463  				Version: testVersion,
   464  			},
   465  		},
   466  		{
   467  			name:      "v2 listener resource",
   468  			resources: []*anypb.Any{v2Lis},
   469  			wantUpdate: map[string]ListenerUpdate{
   470  				v2LDSTarget: {RouteConfigName: v2RouteConfigName, Raw: v2Lis},
   471  			},
   472  			wantMD: UpdateMetadata{
   473  				Status:  ServiceStatusACKed,
   474  				Version: testVersion,
   475  			},
   476  		},
   477  		{
   478  			name:      "v3 listener resource",
   479  			resources: []*anypb.Any{v3LisWithFilters()},
   480  			wantUpdate: map[string]ListenerUpdate{
   481  				v3LDSTarget: {RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters()},
   482  			},
   483  			wantMD: UpdateMetadata{
   484  				Status:  ServiceStatusACKed,
   485  				Version: testVersion,
   486  			},
   487  		},
   488  		{
   489  			name:      "v3 listener with inline route configuration",
   490  			resources: []*anypb.Any{v3LisWithInlineRoute},
   491  			wantUpdate: map[string]ListenerUpdate{
   492  				v3LDSTarget: {
   493  					InlineRouteConfig: &RouteConfigUpdate{
   494  						VirtualHosts: []*VirtualHost{{
   495  							Domains: []string{v3LDSTarget},
   496  							Routes:  []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, RouteAction: RouteActionRoute}},
   497  						}}},
   498  					MaxStreamDuration: time.Second,
   499  					Raw:               v3LisWithInlineRoute,
   500  				},
   501  			},
   502  			wantMD: UpdateMetadata{
   503  				Status:  ServiceStatusACKed,
   504  				Version: testVersion,
   505  			},
   506  		},
   507  		{
   508  			name:      "multiple listener resources",
   509  			resources: []*anypb.Any{v2Lis, v3LisWithFilters()},
   510  			wantUpdate: map[string]ListenerUpdate{
   511  				v2LDSTarget: {RouteConfigName: v2RouteConfigName, Raw: v2Lis},
   512  				v3LDSTarget: {RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters()},
   513  			},
   514  			wantMD: UpdateMetadata{
   515  				Status:  ServiceStatusACKed,
   516  				Version: testVersion,
   517  			},
   518  		},
   519  		{
   520  			// To test that unmarshal keeps processing on errors.
   521  			name: "good and bad listener resources",
   522  			resources: []*anypb.Any{
   523  				v2Lis,
   524  				testutils.MarshalAny(&v3listenerpb.Listener{
   525  					Name: "bad",
   526  					ApiListener: &v3listenerpb.ApiListener{
   527  						ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   528  							RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
   529  						}),
   530  					}}),
   531  				v3LisWithFilters(),
   532  			},
   533  			wantUpdate: map[string]ListenerUpdate{
   534  				v2LDSTarget: {RouteConfigName: v2RouteConfigName, Raw: v2Lis},
   535  				v3LDSTarget: {RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters()},
   536  				"bad":       {},
   537  			},
   538  			wantMD:  errMD,
   539  			wantErr: true,
   540  		},
   541  	}
   542  
   543  	for _, test := range tests {
   544  		t.Run(test.name, func(t *testing.T) {
   545  			update, md, err := UnmarshalListener(testVersion, test.resources, nil)
   546  			if (err != nil) != test.wantErr {
   547  				t.Fatalf("UnmarshalListener(), got err: %v, wantErr: %v", err, test.wantErr)
   548  			}
   549  			if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
   550  				t.Errorf("got unexpected update, diff (-got +want): %v", diff)
   551  			}
   552  			if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" {
   553  				t.Errorf("got unexpected metadata, diff (-got +want): %v", diff)
   554  			}
   555  		})
   556  	}
   557  }
   558  
   559  func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
   560  	const (
   561  		v3LDSTarget = "grpc/server?xds.resource.listening_address=0.0.0.0:9999"
   562  		testVersion = "test-version-lds-server"
   563  	)
   564  
   565  	var (
   566  		routeConfig = &v3routepb.RouteConfiguration{
   567  			Name: "routeName",
   568  			VirtualHosts: []*v3routepb.VirtualHost{{
   569  				Domains: []string{"lds.target.good:3333"},
   570  				Routes: []*v3routepb.Route{{
   571  					Match: &v3routepb.RouteMatch{
   572  						PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
   573  					},
   574  					Action: &v3routepb.Route_NonForwardingAction{},
   575  				}}}}}
   576  		inlineRouteConfig = &RouteConfigUpdate{
   577  			VirtualHosts: []*VirtualHost{{
   578  				Domains: []string{"lds.target.good:3333"},
   579  				Routes:  []*Route{{Prefix: newStringP("/"), RouteAction: RouteActionNonForwardingAction}},
   580  			}}}
   581  		emptyValidNetworkFilters = []*v3listenerpb.Filter{
   582  			{
   583  				Name: "filter-1",
   584  				ConfigType: &v3listenerpb.Filter_TypedConfig{
   585  					TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   586  						RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   587  							RouteConfig: routeConfig,
   588  						},
   589  					}),
   590  				},
   591  			},
   592  		}
   593  		localSocketAddress = &v3corepb.Address{
   594  			Address: &v3corepb.Address_SocketAddress{
   595  				SocketAddress: &v3corepb.SocketAddress{
   596  					Address: "0.0.0.0",
   597  					PortSpecifier: &v3corepb.SocketAddress_PortValue{
   598  						PortValue: 9999,
   599  					},
   600  				},
   601  			},
   602  		}
   603  		listenerEmptyTransportSocket = testutils.MarshalAny(&v3listenerpb.Listener{
   604  			Name:    v3LDSTarget,
   605  			Address: localSocketAddress,
   606  			FilterChains: []*v3listenerpb.FilterChain{
   607  				{
   608  					Name:    "filter-chain-1",
   609  					Filters: emptyValidNetworkFilters,
   610  				},
   611  			},
   612  		})
   613  		listenerNoValidationContext = testutils.MarshalAny(&v3listenerpb.Listener{
   614  			Name:    v3LDSTarget,
   615  			Address: localSocketAddress,
   616  			FilterChains: []*v3listenerpb.FilterChain{
   617  				{
   618  					Name:    "filter-chain-1",
   619  					Filters: emptyValidNetworkFilters,
   620  					TransportSocket: &v3corepb.TransportSocket{
   621  						Name: "envoy.transport_sockets.tls",
   622  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   623  							TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
   624  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   625  									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   626  										InstanceName:    "identityPluginInstance",
   627  										CertificateName: "identityCertName",
   628  									},
   629  								},
   630  							}),
   631  						},
   632  					},
   633  				},
   634  			},
   635  			DefaultFilterChain: &v3listenerpb.FilterChain{
   636  				Name:    "default-filter-chain-1",
   637  				Filters: emptyValidNetworkFilters,
   638  				TransportSocket: &v3corepb.TransportSocket{
   639  					Name: "envoy.transport_sockets.tls",
   640  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   641  						TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
   642  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   643  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   644  									InstanceName:    "defaultIdentityPluginInstance",
   645  									CertificateName: "defaultIdentityCertName",
   646  								},
   647  							},
   648  						}),
   649  					},
   650  				},
   651  			},
   652  		})
   653  		listenerWithValidationContext = testutils.MarshalAny(&v3listenerpb.Listener{
   654  			Name:    v3LDSTarget,
   655  			Address: localSocketAddress,
   656  			FilterChains: []*v3listenerpb.FilterChain{
   657  				{
   658  					Name:    "filter-chain-1",
   659  					Filters: emptyValidNetworkFilters,
   660  					TransportSocket: &v3corepb.TransportSocket{
   661  						Name: "envoy.transport_sockets.tls",
   662  						ConfigType: &v3corepb.TransportSocket_TypedConfig{
   663  							TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
   664  								RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   665  								CommonTlsContext: &v3tlspb.CommonTlsContext{
   666  									TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   667  										InstanceName:    "identityPluginInstance",
   668  										CertificateName: "identityCertName",
   669  									},
   670  									ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   671  										ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   672  											InstanceName:    "rootPluginInstance",
   673  											CertificateName: "rootCertName",
   674  										},
   675  									},
   676  								},
   677  							}),
   678  						},
   679  					},
   680  				},
   681  			},
   682  			DefaultFilterChain: &v3listenerpb.FilterChain{
   683  				Name:    "default-filter-chain-1",
   684  				Filters: emptyValidNetworkFilters,
   685  				TransportSocket: &v3corepb.TransportSocket{
   686  					Name: "envoy.transport_sockets.tls",
   687  					ConfigType: &v3corepb.TransportSocket_TypedConfig{
   688  						TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
   689  							RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
   690  							CommonTlsContext: &v3tlspb.CommonTlsContext{
   691  								TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   692  									InstanceName:    "defaultIdentityPluginInstance",
   693  									CertificateName: "defaultIdentityCertName",
   694  								},
   695  								ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
   696  									ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
   697  										InstanceName:    "defaultRootPluginInstance",
   698  										CertificateName: "defaultRootCertName",
   699  									},
   700  								},
   701  							},
   702  						}),
   703  					},
   704  				},
   705  			},
   706  		})
   707  		errMD = UpdateMetadata{
   708  			Status:  ServiceStatusNACKed,
   709  			Version: testVersion,
   710  			ErrState: &UpdateErrorMetadata{
   711  				Version: testVersion,
   712  				Err:     errPlaceHolder,
   713  			},
   714  		}
   715  	)
   716  
   717  	tests := []struct {
   718  		name       string
   719  		resources  []*anypb.Any
   720  		wantUpdate map[string]ListenerUpdate
   721  		wantMD     UpdateMetadata
   722  		wantErr    string
   723  	}{
   724  		{
   725  			name: "non-empty listener filters",
   726  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   727  				Name: v3LDSTarget,
   728  				ListenerFilters: []*v3listenerpb.ListenerFilter{
   729  					{Name: "listener-filter-1"},
   730  				},
   731  			})},
   732  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   733  			wantMD:     errMD,
   734  			wantErr:    "unsupported field 'listener_filters'",
   735  		},
   736  		{
   737  			name: "use_original_dst is set",
   738  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   739  				Name:           v3LDSTarget,
   740  				UseOriginalDst: &wrapperspb.BoolValue{Value: true},
   741  			})},
   742  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   743  			wantMD:     errMD,
   744  			wantErr:    "unsupported field 'use_original_dst'",
   745  		},
   746  		{
   747  			name:       "no address field",
   748  			resources:  []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{Name: v3LDSTarget})},
   749  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   750  			wantMD:     errMD,
   751  			wantErr:    "no address field in LDS response",
   752  		},
   753  		{
   754  			name: "no socket address field",
   755  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   756  				Name:    v3LDSTarget,
   757  				Address: &v3corepb.Address{},
   758  			})},
   759  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   760  			wantMD:     errMD,
   761  			wantErr:    "no socket_address field in LDS response",
   762  		},
   763  		{
   764  			name: "no filter chains and no default filter chain",
   765  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   766  				Name:    v3LDSTarget,
   767  				Address: localSocketAddress,
   768  				FilterChains: []*v3listenerpb.FilterChain{
   769  					{
   770  						FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}},
   771  						Filters:          emptyValidNetworkFilters,
   772  					},
   773  				},
   774  			})},
   775  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   776  			wantMD:     errMD,
   777  			wantErr:    "no supported filter chains and no default filter chain",
   778  		},
   779  		{
   780  			name: "missing http connection manager network filter",
   781  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   782  				Name:    v3LDSTarget,
   783  				Address: localSocketAddress,
   784  				FilterChains: []*v3listenerpb.FilterChain{
   785  					{
   786  						Name: "filter-chain-1",
   787  					},
   788  				},
   789  			})},
   790  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   791  			wantMD:     errMD,
   792  			wantErr:    "missing HttpConnectionManager filter",
   793  		},
   794  		{
   795  			name: "missing filter name in http filter",
   796  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   797  				Name:    v3LDSTarget,
   798  				Address: localSocketAddress,
   799  				FilterChains: []*v3listenerpb.FilterChain{
   800  					{
   801  						Name: "filter-chain-1",
   802  						Filters: []*v3listenerpb.Filter{
   803  							{
   804  								ConfigType: &v3listenerpb.Filter_TypedConfig{
   805  									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}),
   806  								},
   807  							},
   808  						},
   809  					},
   810  				},
   811  			})},
   812  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   813  			wantMD:     errMD,
   814  			wantErr:    "missing name field in filter",
   815  		},
   816  		{
   817  			name: "duplicate filter names in http filter",
   818  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   819  				Name:    v3LDSTarget,
   820  				Address: localSocketAddress,
   821  				FilterChains: []*v3listenerpb.FilterChain{
   822  					{
   823  						Name: "filter-chain-1",
   824  						Filters: []*v3listenerpb.Filter{
   825  							{
   826  								Name: "name",
   827  								ConfigType: &v3listenerpb.Filter_TypedConfig{
   828  									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   829  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   830  											RouteConfig: routeConfig,
   831  										},
   832  									}),
   833  								},
   834  							},
   835  							{
   836  								Name: "name",
   837  								ConfigType: &v3listenerpb.Filter_TypedConfig{
   838  									TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
   839  										RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
   840  											RouteConfig: routeConfig,
   841  										},
   842  									}),
   843  								},
   844  							},
   845  						},
   846  					},
   847  				},
   848  			})},
   849  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   850  			wantMD:     errMD,
   851  			wantErr:    "duplicate filter name",
   852  		},
   853  		{
   854  			name: "unsupported oneof in typed config of http filter",
   855  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   856  				Name:    v3LDSTarget,
   857  				Address: localSocketAddress,
   858  				FilterChains: []*v3listenerpb.FilterChain{
   859  					{
   860  						Name: "filter-chain-1",
   861  						Filters: []*v3listenerpb.Filter{
   862  							{
   863  								Name:       "name",
   864  								ConfigType: &v3listenerpb.Filter_ConfigDiscovery{},
   865  							},
   866  						},
   867  					},
   868  				},
   869  			})},
   870  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   871  			wantMD:     errMD,
   872  			wantErr:    "unsupported config_type",
   873  		},
   874  		{
   875  			name: "overlapping filter chain match criteria",
   876  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   877  				Name:    v3LDSTarget,
   878  				Address: localSocketAddress,
   879  				FilterChains: []*v3listenerpb.FilterChain{
   880  					{
   881  						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}},
   882  						Filters:          emptyValidNetworkFilters,
   883  					},
   884  					{
   885  						FilterChainMatch: &v3listenerpb.FilterChainMatch{},
   886  						Filters:          emptyValidNetworkFilters,
   887  					},
   888  					{
   889  						FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}},
   890  						Filters:          emptyValidNetworkFilters,
   891  					},
   892  				},
   893  			})},
   894  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   895  			wantMD:     errMD,
   896  			wantErr:    "multiple filter chains with overlapping matching rules are defined",
   897  		},
   898  		{
   899  			name: "unsupported network filter",
   900  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   901  				Name:    v3LDSTarget,
   902  				Address: localSocketAddress,
   903  				FilterChains: []*v3listenerpb.FilterChain{
   904  					{
   905  						Name: "filter-chain-1",
   906  						Filters: []*v3listenerpb.Filter{
   907  							{
   908  								Name: "name",
   909  								ConfigType: &v3listenerpb.Filter_TypedConfig{
   910  									TypedConfig: testutils.MarshalAny(&v3httppb.LocalReplyConfig{}),
   911  								},
   912  							},
   913  						},
   914  					},
   915  				},
   916  			})},
   917  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   918  			wantMD:     errMD,
   919  			wantErr:    "unsupported network filter",
   920  		},
   921  		{
   922  			name: "badly marshaled network filter",
   923  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   924  				Name:    v3LDSTarget,
   925  				Address: localSocketAddress,
   926  				FilterChains: []*v3listenerpb.FilterChain{
   927  					{
   928  						Name: "filter-chain-1",
   929  						Filters: []*v3listenerpb.Filter{
   930  							{
   931  								Name: "name",
   932  								ConfigType: &v3listenerpb.Filter_TypedConfig{
   933  									TypedConfig: &anypb.Any{
   934  										TypeUrl: version.V3HTTPConnManagerURL,
   935  										Value:   []byte{1, 2, 3, 4},
   936  									},
   937  								},
   938  							},
   939  						},
   940  					},
   941  				},
   942  			})},
   943  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   944  			wantMD:     errMD,
   945  			wantErr:    "failed unmarshaling of network filter",
   946  		},
   947  		{
   948  			name: "unexpected transport socket name",
   949  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   950  				Name:    v3LDSTarget,
   951  				Address: localSocketAddress,
   952  				FilterChains: []*v3listenerpb.FilterChain{
   953  					{
   954  						Name:    "filter-chain-1",
   955  						Filters: emptyValidNetworkFilters,
   956  						TransportSocket: &v3corepb.TransportSocket{
   957  							Name: "unsupported-transport-socket-name",
   958  						},
   959  					},
   960  				},
   961  			})},
   962  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   963  			wantMD:     errMD,
   964  			wantErr:    "transport_socket field has unexpected name",
   965  		},
   966  		{
   967  			name: "unexpected transport socket typedConfig URL",
   968  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   969  				Name:    v3LDSTarget,
   970  				Address: localSocketAddress,
   971  				FilterChains: []*v3listenerpb.FilterChain{
   972  					{
   973  						Name:    "filter-chain-1",
   974  						Filters: emptyValidNetworkFilters,
   975  						TransportSocket: &v3corepb.TransportSocket{
   976  							Name: "envoy.transport_sockets.tls",
   977  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
   978  								TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{}),
   979  							},
   980  						},
   981  					},
   982  				},
   983  			})},
   984  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
   985  			wantMD:     errMD,
   986  			wantErr:    "transport_socket field has unexpected typeURL",
   987  		},
   988  		{
   989  			name: "badly marshaled transport socket",
   990  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
   991  				Name:    v3LDSTarget,
   992  				Address: localSocketAddress,
   993  				FilterChains: []*v3listenerpb.FilterChain{
   994  					{
   995  						Name:    "filter-chain-1",
   996  						Filters: emptyValidNetworkFilters,
   997  						TransportSocket: &v3corepb.TransportSocket{
   998  							Name: "envoy.transport_sockets.tls",
   999  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1000  								TypedConfig: &anypb.Any{
  1001  									TypeUrl: version.V3DownstreamTLSContextURL,
  1002  									Value:   []byte{1, 2, 3, 4},
  1003  								},
  1004  							},
  1005  						},
  1006  					},
  1007  				},
  1008  			})},
  1009  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
  1010  			wantMD:     errMD,
  1011  			wantErr:    "failed to unmarshal DownstreamTlsContext in LDS response",
  1012  		},
  1013  		{
  1014  			name: "missing CommonTlsContext",
  1015  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
  1016  				Name:    v3LDSTarget,
  1017  				Address: localSocketAddress,
  1018  				FilterChains: []*v3listenerpb.FilterChain{
  1019  					{
  1020  						Name:    "filter-chain-1",
  1021  						Filters: emptyValidNetworkFilters,
  1022  						TransportSocket: &v3corepb.TransportSocket{
  1023  							Name: "envoy.transport_sockets.tls",
  1024  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1025  								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{}),
  1026  							},
  1027  						},
  1028  					},
  1029  				},
  1030  			})},
  1031  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
  1032  			wantMD:     errMD,
  1033  			wantErr:    "DownstreamTlsContext in LDS response does not contain a CommonTlsContext",
  1034  		},
  1035  		{
  1036  			name: "unsupported validation context in transport socket",
  1037  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
  1038  				Name:    v3LDSTarget,
  1039  				Address: localSocketAddress,
  1040  				FilterChains: []*v3listenerpb.FilterChain{
  1041  					{
  1042  						Name:    "filter-chain-1",
  1043  						Filters: emptyValidNetworkFilters,
  1044  						TransportSocket: &v3corepb.TransportSocket{
  1045  							Name: "envoy.transport_sockets.tls",
  1046  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1047  								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
  1048  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1049  										ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
  1050  											ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
  1051  												Name: "foo-sds-secret",
  1052  											},
  1053  										},
  1054  									},
  1055  								}),
  1056  							},
  1057  						},
  1058  					},
  1059  				},
  1060  			})},
  1061  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
  1062  			wantMD:     errMD,
  1063  			wantErr:    "validation context contains unexpected type",
  1064  		},
  1065  		{
  1066  			name:      "empty transport socket",
  1067  			resources: []*anypb.Any{listenerEmptyTransportSocket},
  1068  			wantUpdate: map[string]ListenerUpdate{
  1069  				v3LDSTarget: {
  1070  					InboundListenerCfg: &InboundListenerConfig{
  1071  						Address: "0.0.0.0",
  1072  						Port:    "9999",
  1073  						FilterChains: &FilterChainManager{
  1074  							dstPrefixMap: map[string]*destPrefixEntry{
  1075  								unspecifiedPrefixMapKey: {
  1076  									srcTypeArr: [3]*sourcePrefixes{
  1077  										{
  1078  											srcPrefixMap: map[string]*sourcePrefixEntry{
  1079  												unspecifiedPrefixMapKey: {
  1080  													srcPortMap: map[int]*FilterChain{
  1081  														0: {InlineRouteConfig: inlineRouteConfig},
  1082  													},
  1083  												},
  1084  											},
  1085  										},
  1086  									},
  1087  								},
  1088  							},
  1089  						},
  1090  					},
  1091  					Raw: listenerEmptyTransportSocket,
  1092  				},
  1093  			},
  1094  			wantMD: UpdateMetadata{
  1095  				Status:  ServiceStatusACKed,
  1096  				Version: testVersion,
  1097  			},
  1098  		},
  1099  		{
  1100  			name: "no identity and root certificate providers",
  1101  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
  1102  				Name:    v3LDSTarget,
  1103  				Address: localSocketAddress,
  1104  				FilterChains: []*v3listenerpb.FilterChain{
  1105  					{
  1106  						Name:    "filter-chain-1",
  1107  						Filters: emptyValidNetworkFilters,
  1108  						TransportSocket: &v3corepb.TransportSocket{
  1109  							Name: "envoy.transport_sockets.tls",
  1110  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1111  								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
  1112  									RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
  1113  									CommonTlsContext: &v3tlspb.CommonTlsContext{
  1114  										TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
  1115  											InstanceName:    "identityPluginInstance",
  1116  											CertificateName: "identityCertName",
  1117  										},
  1118  									},
  1119  								}),
  1120  							},
  1121  						},
  1122  					},
  1123  				},
  1124  			})},
  1125  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
  1126  			wantMD:     errMD,
  1127  			wantErr:    "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
  1128  		},
  1129  		{
  1130  			name: "no identity certificate provider with require_client_cert",
  1131  			resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{
  1132  				Name:    v3LDSTarget,
  1133  				Address: localSocketAddress,
  1134  				FilterChains: []*v3listenerpb.FilterChain{
  1135  					{
  1136  						Name:    "filter-chain-1",
  1137  						Filters: emptyValidNetworkFilters,
  1138  						TransportSocket: &v3corepb.TransportSocket{
  1139  							Name: "envoy.transport_sockets.tls",
  1140  							ConfigType: &v3corepb.TransportSocket_TypedConfig{
  1141  								TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
  1142  									CommonTlsContext: &v3tlspb.CommonTlsContext{},
  1143  								}),
  1144  							},
  1145  						},
  1146  					},
  1147  				},
  1148  			})},
  1149  			wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}},
  1150  			wantMD:     errMD,
  1151  			wantErr:    "security configuration on the server-side does not contain identity certificate provider instance name",
  1152  		},
  1153  		{
  1154  			name:      "happy case with no validation context",
  1155  			resources: []*anypb.Any{listenerNoValidationContext},
  1156  			wantUpdate: map[string]ListenerUpdate{
  1157  				v3LDSTarget: {
  1158  					InboundListenerCfg: &InboundListenerConfig{
  1159  						Address: "0.0.0.0",
  1160  						Port:    "9999",
  1161  						FilterChains: &FilterChainManager{
  1162  							dstPrefixMap: map[string]*destPrefixEntry{
  1163  								unspecifiedPrefixMapKey: {
  1164  									srcTypeArr: [3]*sourcePrefixes{
  1165  										{
  1166  											srcPrefixMap: map[string]*sourcePrefixEntry{
  1167  												unspecifiedPrefixMapKey: {
  1168  													srcPortMap: map[int]*FilterChain{
  1169  														0: {
  1170  															SecurityCfg: &SecurityConfig{
  1171  																IdentityInstanceName: "identityPluginInstance",
  1172  																IdentityCertName:     "identityCertName",
  1173  															},
  1174  															InlineRouteConfig: inlineRouteConfig,
  1175  														},
  1176  													},
  1177  												},
  1178  											},
  1179  										},
  1180  									},
  1181  								},
  1182  							},
  1183  							def: &FilterChain{
  1184  								SecurityCfg: &SecurityConfig{
  1185  									IdentityInstanceName: "defaultIdentityPluginInstance",
  1186  									IdentityCertName:     "defaultIdentityCertName",
  1187  								},
  1188  								InlineRouteConfig: inlineRouteConfig,
  1189  							},
  1190  						},
  1191  					},
  1192  					Raw: listenerNoValidationContext,
  1193  				},
  1194  			},
  1195  			wantMD: UpdateMetadata{
  1196  				Status:  ServiceStatusACKed,
  1197  				Version: testVersion,
  1198  			},
  1199  		},
  1200  		{
  1201  			name:      "happy case with validation context provider instance",
  1202  			resources: []*anypb.Any{listenerWithValidationContext},
  1203  			wantUpdate: map[string]ListenerUpdate{
  1204  				v3LDSTarget: {
  1205  					InboundListenerCfg: &InboundListenerConfig{
  1206  						Address: "0.0.0.0",
  1207  						Port:    "9999",
  1208  						FilterChains: &FilterChainManager{
  1209  							dstPrefixMap: map[string]*destPrefixEntry{
  1210  								unspecifiedPrefixMapKey: {
  1211  									srcTypeArr: [3]*sourcePrefixes{
  1212  										{
  1213  											srcPrefixMap: map[string]*sourcePrefixEntry{
  1214  												unspecifiedPrefixMapKey: {
  1215  													srcPortMap: map[int]*FilterChain{
  1216  														0: {
  1217  															SecurityCfg: &SecurityConfig{
  1218  																RootInstanceName:     "rootPluginInstance",
  1219  																RootCertName:         "rootCertName",
  1220  																IdentityInstanceName: "identityPluginInstance",
  1221  																IdentityCertName:     "identityCertName",
  1222  																RequireClientCert:    true,
  1223  															},
  1224  															InlineRouteConfig: inlineRouteConfig,
  1225  														},
  1226  													},
  1227  												},
  1228  											},
  1229  										},
  1230  									},
  1231  								},
  1232  							},
  1233  							def: &FilterChain{
  1234  								SecurityCfg: &SecurityConfig{
  1235  									RootInstanceName:     "defaultRootPluginInstance",
  1236  									RootCertName:         "defaultRootCertName",
  1237  									IdentityInstanceName: "defaultIdentityPluginInstance",
  1238  									IdentityCertName:     "defaultIdentityCertName",
  1239  									RequireClientCert:    true,
  1240  								},
  1241  								InlineRouteConfig: inlineRouteConfig,
  1242  							},
  1243  						},
  1244  					},
  1245  					Raw: listenerWithValidationContext,
  1246  				},
  1247  			},
  1248  			wantMD: UpdateMetadata{
  1249  				Status:  ServiceStatusACKed,
  1250  				Version: testVersion,
  1251  			},
  1252  		},
  1253  	}
  1254  
  1255  	for _, test := range tests {
  1256  		t.Run(test.name, func(t *testing.T) {
  1257  			gotUpdate, md, err := UnmarshalListener(testVersion, test.resources, nil)
  1258  			if (err != nil) != (test.wantErr != "") {
  1259  				t.Fatalf("UnmarshalListener(), got err: %v, wantErr: %v", err, test.wantErr)
  1260  			}
  1261  			if err != nil && !strings.Contains(err.Error(), test.wantErr) {
  1262  				t.Fatalf("UnmarshalListener() = %v wantErr: %q", err, test.wantErr)
  1263  			}
  1264  			if diff := cmp.Diff(gotUpdate, test.wantUpdate, cmpOpts); diff != "" {
  1265  				t.Errorf("got unexpected update, diff (-got +want): %v", diff)
  1266  			}
  1267  			if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" {
  1268  				t.Errorf("got unexpected metadata, diff (-got +want): %v", diff)
  1269  			}
  1270  		})
  1271  	}
  1272  }
  1273  
  1274  type filterConfig struct {
  1275  	httpfilter.FilterConfig
  1276  	Cfg      proto.Message
  1277  	Override proto.Message
  1278  }
  1279  
  1280  // httpFilter allows testing the http filter registry and parsing functionality.
  1281  type httpFilter struct {
  1282  	httpfilter.ClientInterceptorBuilder
  1283  	httpfilter.ServerInterceptorBuilder
  1284  }
  1285  
  1286  func (httpFilter) TypeURLs() []string { return []string{"custom.filter"} }
  1287  
  1288  func (httpFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1289  	return filterConfig{Cfg: cfg}, nil
  1290  }
  1291  
  1292  func (httpFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1293  	return filterConfig{Override: override}, nil
  1294  }
  1295  
  1296  // errHTTPFilter returns errors no matter what is passed to ParseFilterConfig.
  1297  type errHTTPFilter struct {
  1298  	httpfilter.ClientInterceptorBuilder
  1299  }
  1300  
  1301  func (errHTTPFilter) TypeURLs() []string { return []string{"err.custom.filter"} }
  1302  
  1303  func (errHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1304  	return nil, fmt.Errorf("error from ParseFilterConfig")
  1305  }
  1306  
  1307  func (errHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1308  	return nil, fmt.Errorf("error from ParseFilterConfigOverride")
  1309  }
  1310  
  1311  func init() {
  1312  	httpfilter.Register(httpFilter{})
  1313  	httpfilter.Register(errHTTPFilter{})
  1314  	httpfilter.Register(serverOnlyHTTPFilter{})
  1315  	httpfilter.Register(clientOnlyHTTPFilter{})
  1316  }
  1317  
  1318  // serverOnlyHTTPFilter does not implement ClientInterceptorBuilder
  1319  type serverOnlyHTTPFilter struct {
  1320  	httpfilter.ServerInterceptorBuilder
  1321  }
  1322  
  1323  func (serverOnlyHTTPFilter) TypeURLs() []string { return []string{"serverOnly.custom.filter"} }
  1324  
  1325  func (serverOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1326  	return filterConfig{Cfg: cfg}, nil
  1327  }
  1328  
  1329  func (serverOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1330  	return filterConfig{Override: override}, nil
  1331  }
  1332  
  1333  // clientOnlyHTTPFilter does not implement ServerInterceptorBuilder
  1334  type clientOnlyHTTPFilter struct {
  1335  	httpfilter.ClientInterceptorBuilder
  1336  }
  1337  
  1338  func (clientOnlyHTTPFilter) TypeURLs() []string { return []string{"clientOnly.custom.filter"} }
  1339  
  1340  func (clientOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
  1341  	return filterConfig{Cfg: cfg}, nil
  1342  }
  1343  
  1344  func (clientOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
  1345  	return filterConfig{Override: override}, nil
  1346  }
  1347  
  1348  var customFilterConfig = &anypb.Any{
  1349  	TypeUrl: "custom.filter",
  1350  	Value:   []byte{1, 2, 3},
  1351  }
  1352  
  1353  var errFilterConfig = &anypb.Any{
  1354  	TypeUrl: "err.custom.filter",
  1355  	Value:   []byte{1, 2, 3},
  1356  }
  1357  
  1358  var serverOnlyCustomFilterConfig = &anypb.Any{
  1359  	TypeUrl: "serverOnly.custom.filter",
  1360  	Value:   []byte{1, 2, 3},
  1361  }
  1362  
  1363  var clientOnlyCustomFilterConfig = &anypb.Any{
  1364  	TypeUrl: "clientOnly.custom.filter",
  1365  	Value:   []byte{1, 2, 3},
  1366  }
  1367  
  1368  var customFilterTypedStructConfig = &v1typepb.TypedStruct{
  1369  	TypeUrl: "custom.filter",
  1370  	Value: &spb.Struct{
  1371  		Fields: map[string]*spb.Value{
  1372  			"foo": {Kind: &spb.Value_StringValue{StringValue: "bar"}},
  1373  		},
  1374  	},
  1375  }
  1376  var wrappedCustomFilterTypedStructConfig *anypb.Any
  1377  
  1378  func init() {
  1379  	wrappedCustomFilterTypedStructConfig = testutils.MarshalAny(customFilterTypedStructConfig)
  1380  }
  1381  
  1382  var unknownFilterConfig = &anypb.Any{
  1383  	TypeUrl: "unknown.custom.filter",
  1384  	Value:   []byte{1, 2, 3},
  1385  }
  1386  
  1387  func wrappedOptionalFilter(name string) *anypb.Any {
  1388  	return testutils.MarshalAny(&v3routepb.FilterConfig{
  1389  		IsOptional: true,
  1390  		Config: &anypb.Any{
  1391  			TypeUrl: name,
  1392  			Value:   []byte{1, 2, 3},
  1393  		},
  1394  	})
  1395  }