1// +build go1.12 2 3/* 4 * 5 * Copyright 2020 gRPC authors. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 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. 18 * 19 */ 20 21package xdsclient 22 23import ( 24 "fmt" 25 "strings" 26 "testing" 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" 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" 38 "google.golang.org/grpc/xds/internal/version" 39 40 v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" 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 52func (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" 59 testVersion = "test-version-lds-client" 60 ) 61 62 var ( 63 v2Lis = testutils.MarshalAny(&v2xdspb.Listener{ 64 Name: v2LDSTarget, 65 ApiListener: &v2listenerpb.ApiListener{ 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 }, 72 RouteConfigName: v2RouteConfigName, 73 }, 74 }, 75 }), 76 }, 77 }) 78 customFilter = &v3httppb.HttpFilter{ 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}}}}, 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 559func (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 emptyValidNetworkFilters = []*v3listenerpb.Filter{ 567 { 568 Name: "filter-1", 569 ConfigType: &v3listenerpb.Filter_TypedConfig{ 570 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}), 571 }, 572 }, 573 } 574 localSocketAddress = &v3corepb.Address{ 575 Address: &v3corepb.Address_SocketAddress{ 576 SocketAddress: &v3corepb.SocketAddress{ 577 Address: "0.0.0.0", 578 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 579 PortValue: 9999, 580 }, 581 }, 582 }, 583 } 584 listenerEmptyTransportSocket = testutils.MarshalAny(&v3listenerpb.Listener{ 585 Name: v3LDSTarget, 586 Address: localSocketAddress, 587 FilterChains: []*v3listenerpb.FilterChain{ 588 { 589 Name: "filter-chain-1", 590 Filters: emptyValidNetworkFilters, 591 }, 592 }, 593 }) 594 listenerNoValidationContext = testutils.MarshalAny(&v3listenerpb.Listener{ 595 Name: v3LDSTarget, 596 Address: localSocketAddress, 597 FilterChains: []*v3listenerpb.FilterChain{ 598 { 599 Name: "filter-chain-1", 600 Filters: emptyValidNetworkFilters, 601 TransportSocket: &v3corepb.TransportSocket{ 602 Name: "envoy.transport_sockets.tls", 603 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 604 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 605 CommonTlsContext: &v3tlspb.CommonTlsContext{ 606 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 607 InstanceName: "identityPluginInstance", 608 CertificateName: "identityCertName", 609 }, 610 }, 611 }), 612 }, 613 }, 614 }, 615 }, 616 DefaultFilterChain: &v3listenerpb.FilterChain{ 617 Name: "default-filter-chain-1", 618 Filters: emptyValidNetworkFilters, 619 TransportSocket: &v3corepb.TransportSocket{ 620 Name: "envoy.transport_sockets.tls", 621 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 622 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 623 CommonTlsContext: &v3tlspb.CommonTlsContext{ 624 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 625 InstanceName: "defaultIdentityPluginInstance", 626 CertificateName: "defaultIdentityCertName", 627 }, 628 }, 629 }), 630 }, 631 }, 632 }, 633 }) 634 listenerWithValidationContext = testutils.MarshalAny(&v3listenerpb.Listener{ 635 Name: v3LDSTarget, 636 Address: localSocketAddress, 637 FilterChains: []*v3listenerpb.FilterChain{ 638 { 639 Name: "filter-chain-1", 640 Filters: emptyValidNetworkFilters, 641 TransportSocket: &v3corepb.TransportSocket{ 642 Name: "envoy.transport_sockets.tls", 643 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 644 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 645 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 646 CommonTlsContext: &v3tlspb.CommonTlsContext{ 647 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 648 InstanceName: "identityPluginInstance", 649 CertificateName: "identityCertName", 650 }, 651 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 652 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 653 InstanceName: "rootPluginInstance", 654 CertificateName: "rootCertName", 655 }, 656 }, 657 }, 658 }), 659 }, 660 }, 661 }, 662 }, 663 DefaultFilterChain: &v3listenerpb.FilterChain{ 664 Name: "default-filter-chain-1", 665 Filters: emptyValidNetworkFilters, 666 TransportSocket: &v3corepb.TransportSocket{ 667 Name: "envoy.transport_sockets.tls", 668 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 669 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 670 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 671 CommonTlsContext: &v3tlspb.CommonTlsContext{ 672 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 673 InstanceName: "defaultIdentityPluginInstance", 674 CertificateName: "defaultIdentityCertName", 675 }, 676 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 677 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 678 InstanceName: "defaultRootPluginInstance", 679 CertificateName: "defaultRootCertName", 680 }, 681 }, 682 }, 683 }), 684 }, 685 }, 686 }, 687 }) 688 errMD = UpdateMetadata{ 689 Status: ServiceStatusNACKed, 690 Version: testVersion, 691 ErrState: &UpdateErrorMetadata{ 692 Version: testVersion, 693 Err: errPlaceHolder, 694 }, 695 } 696 ) 697 698 tests := []struct { 699 name string 700 resources []*anypb.Any 701 wantUpdate map[string]ListenerUpdate 702 wantMD UpdateMetadata 703 wantErr string 704 }{ 705 { 706 name: "non-empty listener filters", 707 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 708 Name: v3LDSTarget, 709 ListenerFilters: []*v3listenerpb.ListenerFilter{ 710 {Name: "listener-filter-1"}, 711 }, 712 })}, 713 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 714 wantMD: errMD, 715 wantErr: "unsupported field 'listener_filters'", 716 }, 717 { 718 name: "use_original_dst is set", 719 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 720 Name: v3LDSTarget, 721 UseOriginalDst: &wrapperspb.BoolValue{Value: true}, 722 })}, 723 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 724 wantMD: errMD, 725 wantErr: "unsupported field 'use_original_dst'", 726 }, 727 { 728 name: "no address field", 729 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{Name: v3LDSTarget})}, 730 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 731 wantMD: errMD, 732 wantErr: "no address field in LDS response", 733 }, 734 { 735 name: "no socket address field", 736 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 737 Name: v3LDSTarget, 738 Address: &v3corepb.Address{}, 739 })}, 740 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 741 wantMD: errMD, 742 wantErr: "no socket_address field in LDS response", 743 }, 744 { 745 name: "no filter chains and no default filter chain", 746 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 747 Name: v3LDSTarget, 748 Address: localSocketAddress, 749 FilterChains: []*v3listenerpb.FilterChain{ 750 { 751 FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}}, 752 Filters: emptyValidNetworkFilters, 753 }, 754 }, 755 })}, 756 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 757 wantMD: errMD, 758 wantErr: "no supported filter chains and no default filter chain", 759 }, 760 { 761 name: "missing http connection manager network filter", 762 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 763 Name: v3LDSTarget, 764 Address: localSocketAddress, 765 FilterChains: []*v3listenerpb.FilterChain{ 766 { 767 Name: "filter-chain-1", 768 }, 769 }, 770 })}, 771 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 772 wantMD: errMD, 773 wantErr: "missing HttpConnectionManager filter", 774 }, 775 { 776 name: "missing filter name in http filter", 777 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 778 Name: v3LDSTarget, 779 Address: localSocketAddress, 780 FilterChains: []*v3listenerpb.FilterChain{ 781 { 782 Name: "filter-chain-1", 783 Filters: []*v3listenerpb.Filter{ 784 { 785 ConfigType: &v3listenerpb.Filter_TypedConfig{ 786 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}), 787 }, 788 }, 789 }, 790 }, 791 }, 792 })}, 793 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 794 wantMD: errMD, 795 wantErr: "missing name field in filter", 796 }, 797 { 798 name: "duplicate filter names in http filter", 799 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 800 Name: v3LDSTarget, 801 Address: localSocketAddress, 802 FilterChains: []*v3listenerpb.FilterChain{ 803 { 804 Name: "filter-chain-1", 805 Filters: []*v3listenerpb.Filter{ 806 { 807 Name: "name", 808 ConfigType: &v3listenerpb.Filter_TypedConfig{ 809 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}), 810 }, 811 }, 812 { 813 Name: "name", 814 ConfigType: &v3listenerpb.Filter_TypedConfig{ 815 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}), 816 }, 817 }, 818 }, 819 }, 820 }, 821 })}, 822 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 823 wantMD: errMD, 824 wantErr: "duplicate filter name", 825 }, 826 { 827 name: "unsupported oneof in typed config of http filter", 828 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 829 Name: v3LDSTarget, 830 Address: localSocketAddress, 831 FilterChains: []*v3listenerpb.FilterChain{ 832 { 833 Name: "filter-chain-1", 834 Filters: []*v3listenerpb.Filter{ 835 { 836 Name: "name", 837 ConfigType: &v3listenerpb.Filter_ConfigDiscovery{}, 838 }, 839 }, 840 }, 841 }, 842 })}, 843 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 844 wantMD: errMD, 845 wantErr: "unsupported config_type", 846 }, 847 { 848 name: "overlapping filter chain match criteria", 849 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 850 Name: v3LDSTarget, 851 Address: localSocketAddress, 852 FilterChains: []*v3listenerpb.FilterChain{ 853 { 854 FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}}, 855 Filters: emptyValidNetworkFilters, 856 }, 857 { 858 FilterChainMatch: &v3listenerpb.FilterChainMatch{}, 859 Filters: emptyValidNetworkFilters, 860 }, 861 { 862 FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}}, 863 Filters: emptyValidNetworkFilters, 864 }, 865 }, 866 })}, 867 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 868 wantMD: errMD, 869 wantErr: "multiple filter chains with overlapping matching rules are defined", 870 }, 871 { 872 name: "unsupported network filter", 873 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 874 Name: v3LDSTarget, 875 Address: localSocketAddress, 876 FilterChains: []*v3listenerpb.FilterChain{ 877 { 878 Name: "filter-chain-1", 879 Filters: []*v3listenerpb.Filter{ 880 { 881 Name: "name", 882 ConfigType: &v3listenerpb.Filter_TypedConfig{ 883 TypedConfig: testutils.MarshalAny(&v3httppb.LocalReplyConfig{}), 884 }, 885 }, 886 }, 887 }, 888 }, 889 })}, 890 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 891 wantMD: errMD, 892 wantErr: "unsupported network filter", 893 }, 894 { 895 name: "badly marshaled network filter", 896 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 897 Name: v3LDSTarget, 898 Address: localSocketAddress, 899 FilterChains: []*v3listenerpb.FilterChain{ 900 { 901 Name: "filter-chain-1", 902 Filters: []*v3listenerpb.Filter{ 903 { 904 Name: "name", 905 ConfigType: &v3listenerpb.Filter_TypedConfig{ 906 TypedConfig: &anypb.Any{ 907 TypeUrl: version.V3HTTPConnManagerURL, 908 Value: []byte{1, 2, 3, 4}, 909 }, 910 }, 911 }, 912 }, 913 }, 914 }, 915 })}, 916 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 917 wantMD: errMD, 918 wantErr: "failed unmarshaling of network filter", 919 }, 920 { 921 name: "unexpected transport socket name", 922 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 923 Name: v3LDSTarget, 924 Address: localSocketAddress, 925 FilterChains: []*v3listenerpb.FilterChain{ 926 { 927 Name: "filter-chain-1", 928 Filters: emptyValidNetworkFilters, 929 TransportSocket: &v3corepb.TransportSocket{ 930 Name: "unsupported-transport-socket-name", 931 }, 932 }, 933 }, 934 })}, 935 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 936 wantMD: errMD, 937 wantErr: "transport_socket field has unexpected name", 938 }, 939 { 940 name: "unexpected transport socket typedConfig URL", 941 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 942 Name: v3LDSTarget, 943 Address: localSocketAddress, 944 FilterChains: []*v3listenerpb.FilterChain{ 945 { 946 Name: "filter-chain-1", 947 Filters: emptyValidNetworkFilters, 948 TransportSocket: &v3corepb.TransportSocket{ 949 Name: "envoy.transport_sockets.tls", 950 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 951 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{}), 952 }, 953 }, 954 }, 955 }, 956 })}, 957 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 958 wantMD: errMD, 959 wantErr: "transport_socket field has unexpected typeURL", 960 }, 961 { 962 name: "badly marshaled transport socket", 963 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 964 Name: v3LDSTarget, 965 Address: localSocketAddress, 966 FilterChains: []*v3listenerpb.FilterChain{ 967 { 968 Name: "filter-chain-1", 969 Filters: emptyValidNetworkFilters, 970 TransportSocket: &v3corepb.TransportSocket{ 971 Name: "envoy.transport_sockets.tls", 972 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 973 TypedConfig: &anypb.Any{ 974 TypeUrl: version.V3DownstreamTLSContextURL, 975 Value: []byte{1, 2, 3, 4}, 976 }, 977 }, 978 }, 979 }, 980 }, 981 })}, 982 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 983 wantMD: errMD, 984 wantErr: "failed to unmarshal DownstreamTlsContext in LDS response", 985 }, 986 { 987 name: "missing CommonTlsContext", 988 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 989 Name: v3LDSTarget, 990 Address: localSocketAddress, 991 FilterChains: []*v3listenerpb.FilterChain{ 992 { 993 Name: "filter-chain-1", 994 Filters: emptyValidNetworkFilters, 995 TransportSocket: &v3corepb.TransportSocket{ 996 Name: "envoy.transport_sockets.tls", 997 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 998 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{}), 999 }, 1000 }, 1001 }, 1002 }, 1003 })}, 1004 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 1005 wantMD: errMD, 1006 wantErr: "DownstreamTlsContext in LDS response does not contain a CommonTlsContext", 1007 }, 1008 { 1009 name: "unsupported validation context in transport socket", 1010 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1011 Name: v3LDSTarget, 1012 Address: localSocketAddress, 1013 FilterChains: []*v3listenerpb.FilterChain{ 1014 { 1015 Name: "filter-chain-1", 1016 Filters: emptyValidNetworkFilters, 1017 TransportSocket: &v3corepb.TransportSocket{ 1018 Name: "envoy.transport_sockets.tls", 1019 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1020 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 1021 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1022 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 1023 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 1024 Name: "foo-sds-secret", 1025 }, 1026 }, 1027 }, 1028 }), 1029 }, 1030 }, 1031 }, 1032 }, 1033 })}, 1034 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 1035 wantMD: errMD, 1036 wantErr: "validation context contains unexpected type", 1037 }, 1038 { 1039 name: "empty transport socket", 1040 resources: []*anypb.Any{listenerEmptyTransportSocket}, 1041 wantUpdate: map[string]ListenerUpdate{ 1042 v3LDSTarget: { 1043 InboundListenerCfg: &InboundListenerConfig{ 1044 Address: "0.0.0.0", 1045 Port: "9999", 1046 FilterChains: &FilterChainManager{ 1047 dstPrefixMap: map[string]*destPrefixEntry{ 1048 unspecifiedPrefixMapKey: { 1049 srcTypeArr: [3]*sourcePrefixes{ 1050 { 1051 srcPrefixMap: map[string]*sourcePrefixEntry{ 1052 unspecifiedPrefixMapKey: { 1053 srcPortMap: map[int]*FilterChain{ 1054 0: {}, 1055 }, 1056 }, 1057 }, 1058 }, 1059 }, 1060 }, 1061 }, 1062 }, 1063 }, 1064 Raw: listenerEmptyTransportSocket, 1065 }, 1066 }, 1067 wantMD: UpdateMetadata{ 1068 Status: ServiceStatusACKed, 1069 Version: testVersion, 1070 }, 1071 }, 1072 { 1073 name: "no identity and root certificate providers", 1074 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1075 Name: v3LDSTarget, 1076 Address: localSocketAddress, 1077 FilterChains: []*v3listenerpb.FilterChain{ 1078 { 1079 Name: "filter-chain-1", 1080 Filters: emptyValidNetworkFilters, 1081 TransportSocket: &v3corepb.TransportSocket{ 1082 Name: "envoy.transport_sockets.tls", 1083 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1084 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 1085 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 1086 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1087 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1088 InstanceName: "identityPluginInstance", 1089 CertificateName: "identityCertName", 1090 }, 1091 }, 1092 }), 1093 }, 1094 }, 1095 }, 1096 }, 1097 })}, 1098 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 1099 wantMD: errMD, 1100 wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set", 1101 }, 1102 { 1103 name: "no identity certificate provider with require_client_cert", 1104 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1105 Name: v3LDSTarget, 1106 Address: localSocketAddress, 1107 FilterChains: []*v3listenerpb.FilterChain{ 1108 { 1109 Name: "filter-chain-1", 1110 Filters: emptyValidNetworkFilters, 1111 TransportSocket: &v3corepb.TransportSocket{ 1112 Name: "envoy.transport_sockets.tls", 1113 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1114 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 1115 CommonTlsContext: &v3tlspb.CommonTlsContext{}, 1116 }), 1117 }, 1118 }, 1119 }, 1120 }, 1121 })}, 1122 wantUpdate: map[string]ListenerUpdate{v3LDSTarget: {}}, 1123 wantMD: errMD, 1124 wantErr: "security configuration on the server-side does not contain identity certificate provider instance name", 1125 }, 1126 { 1127 name: "happy case with no validation context", 1128 resources: []*anypb.Any{listenerNoValidationContext}, 1129 wantUpdate: map[string]ListenerUpdate{ 1130 v3LDSTarget: { 1131 InboundListenerCfg: &InboundListenerConfig{ 1132 Address: "0.0.0.0", 1133 Port: "9999", 1134 FilterChains: &FilterChainManager{ 1135 dstPrefixMap: map[string]*destPrefixEntry{ 1136 unspecifiedPrefixMapKey: { 1137 srcTypeArr: [3]*sourcePrefixes{ 1138 { 1139 srcPrefixMap: map[string]*sourcePrefixEntry{ 1140 unspecifiedPrefixMapKey: { 1141 srcPortMap: map[int]*FilterChain{ 1142 0: { 1143 SecurityCfg: &SecurityConfig{ 1144 IdentityInstanceName: "identityPluginInstance", 1145 IdentityCertName: "identityCertName", 1146 }, 1147 }, 1148 }, 1149 }, 1150 }, 1151 }, 1152 }, 1153 }, 1154 }, 1155 def: &FilterChain{ 1156 SecurityCfg: &SecurityConfig{ 1157 IdentityInstanceName: "defaultIdentityPluginInstance", 1158 IdentityCertName: "defaultIdentityCertName", 1159 }, 1160 }, 1161 }, 1162 }, 1163 Raw: listenerNoValidationContext, 1164 }, 1165 }, 1166 wantMD: UpdateMetadata{ 1167 Status: ServiceStatusACKed, 1168 Version: testVersion, 1169 }, 1170 }, 1171 { 1172 name: "happy case with validation context provider instance", 1173 resources: []*anypb.Any{listenerWithValidationContext}, 1174 wantUpdate: map[string]ListenerUpdate{ 1175 v3LDSTarget: { 1176 InboundListenerCfg: &InboundListenerConfig{ 1177 Address: "0.0.0.0", 1178 Port: "9999", 1179 FilterChains: &FilterChainManager{ 1180 dstPrefixMap: map[string]*destPrefixEntry{ 1181 unspecifiedPrefixMapKey: { 1182 srcTypeArr: [3]*sourcePrefixes{ 1183 { 1184 srcPrefixMap: map[string]*sourcePrefixEntry{ 1185 unspecifiedPrefixMapKey: { 1186 srcPortMap: map[int]*FilterChain{ 1187 0: { 1188 SecurityCfg: &SecurityConfig{ 1189 RootInstanceName: "rootPluginInstance", 1190 RootCertName: "rootCertName", 1191 IdentityInstanceName: "identityPluginInstance", 1192 IdentityCertName: "identityCertName", 1193 RequireClientCert: true, 1194 }, 1195 }, 1196 }, 1197 }, 1198 }, 1199 }, 1200 }, 1201 }, 1202 }, 1203 def: &FilterChain{ 1204 SecurityCfg: &SecurityConfig{ 1205 RootInstanceName: "defaultRootPluginInstance", 1206 RootCertName: "defaultRootCertName", 1207 IdentityInstanceName: "defaultIdentityPluginInstance", 1208 IdentityCertName: "defaultIdentityCertName", 1209 RequireClientCert: true, 1210 }, 1211 }, 1212 }, 1213 }, 1214 Raw: listenerWithValidationContext, 1215 }, 1216 }, 1217 wantMD: UpdateMetadata{ 1218 Status: ServiceStatusACKed, 1219 Version: testVersion, 1220 }, 1221 }, 1222 } 1223 1224 for _, test := range tests { 1225 t.Run(test.name, func(t *testing.T) { 1226 gotUpdate, md, err := UnmarshalListener(testVersion, test.resources, nil) 1227 if (err != nil) != (test.wantErr != "") { 1228 t.Fatalf("UnmarshalListener(), got err: %v, wantErr: %v", err, test.wantErr) 1229 } 1230 if err != nil && !strings.Contains(err.Error(), test.wantErr) { 1231 t.Fatalf("UnmarshalListener() = %v wantErr: %q", err, test.wantErr) 1232 } 1233 if diff := cmp.Diff(gotUpdate, test.wantUpdate, cmpOpts); diff != "" { 1234 t.Errorf("got unexpected update, diff (-got +want): %v", diff) 1235 } 1236 if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" { 1237 t.Errorf("got unexpected metadata, diff (-got +want): %v", diff) 1238 } 1239 }) 1240 } 1241} 1242 1243type filterConfig struct { 1244 httpfilter.FilterConfig 1245 Cfg proto.Message 1246 Override proto.Message 1247} 1248 1249// httpFilter allows testing the http filter registry and parsing functionality. 1250type httpFilter struct { 1251 httpfilter.ClientInterceptorBuilder 1252 httpfilter.ServerInterceptorBuilder 1253} 1254 1255func (httpFilter) TypeURLs() []string { return []string{"custom.filter"} } 1256 1257func (httpFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1258 return filterConfig{Cfg: cfg}, nil 1259} 1260 1261func (httpFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1262 return filterConfig{Override: override}, nil 1263} 1264 1265// errHTTPFilter returns errors no matter what is passed to ParseFilterConfig. 1266type errHTTPFilter struct { 1267 httpfilter.ClientInterceptorBuilder 1268} 1269 1270func (errHTTPFilter) TypeURLs() []string { return []string{"err.custom.filter"} } 1271 1272func (errHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1273 return nil, fmt.Errorf("error from ParseFilterConfig") 1274} 1275 1276func (errHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1277 return nil, fmt.Errorf("error from ParseFilterConfigOverride") 1278} 1279 1280func init() { 1281 httpfilter.Register(httpFilter{}) 1282 httpfilter.Register(errHTTPFilter{}) 1283 httpfilter.Register(serverOnlyHTTPFilter{}) 1284 httpfilter.Register(clientOnlyHTTPFilter{}) 1285} 1286 1287// serverOnlyHTTPFilter does not implement ClientInterceptorBuilder 1288type serverOnlyHTTPFilter struct { 1289 httpfilter.ServerInterceptorBuilder 1290} 1291 1292func (serverOnlyHTTPFilter) TypeURLs() []string { return []string{"serverOnly.custom.filter"} } 1293 1294func (serverOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1295 return filterConfig{Cfg: cfg}, nil 1296} 1297 1298func (serverOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1299 return filterConfig{Override: override}, nil 1300} 1301 1302// clientOnlyHTTPFilter does not implement ServerInterceptorBuilder 1303type clientOnlyHTTPFilter struct { 1304 httpfilter.ClientInterceptorBuilder 1305} 1306 1307func (clientOnlyHTTPFilter) TypeURLs() []string { return []string{"clientOnly.custom.filter"} } 1308 1309func (clientOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1310 return filterConfig{Cfg: cfg}, nil 1311} 1312 1313func (clientOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1314 return filterConfig{Override: override}, nil 1315} 1316 1317var customFilterConfig = &anypb.Any{ 1318 TypeUrl: "custom.filter", 1319 Value: []byte{1, 2, 3}, 1320} 1321 1322var errFilterConfig = &anypb.Any{ 1323 TypeUrl: "err.custom.filter", 1324 Value: []byte{1, 2, 3}, 1325} 1326 1327var serverOnlyCustomFilterConfig = &anypb.Any{ 1328 TypeUrl: "serverOnly.custom.filter", 1329 Value: []byte{1, 2, 3}, 1330} 1331 1332var clientOnlyCustomFilterConfig = &anypb.Any{ 1333 TypeUrl: "clientOnly.custom.filter", 1334 Value: []byte{1, 2, 3}, 1335} 1336 1337var customFilterTypedStructConfig = &v1typepb.TypedStruct{ 1338 TypeUrl: "custom.filter", 1339 Value: &spb.Struct{ 1340 Fields: map[string]*spb.Value{ 1341 "foo": {Kind: &spb.Value_StringValue{StringValue: "bar"}}, 1342 }, 1343 }, 1344} 1345var wrappedCustomFilterTypedStructConfig *anypb.Any 1346 1347func init() { 1348 wrappedCustomFilterTypedStructConfig = testutils.MarshalAny(customFilterTypedStructConfig) 1349} 1350 1351var unknownFilterConfig = &anypb.Any{ 1352 TypeUrl: "unknown.custom.filter", 1353 Value: []byte{1, 2, 3}, 1354} 1355 1356func wrappedOptionalFilter(name string) *anypb.Any { 1357 return testutils.MarshalAny(&v3routepb.FilterConfig{ 1358 IsOptional: true, 1359 Config: &anypb.Any{ 1360 TypeUrl: name, 1361 Value: []byte{1, 2, 3}, 1362 }, 1363 }) 1364} 1365