istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/util/util_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package util 16 17 import ( 18 "fmt" 19 "reflect" 20 "testing" 21 22 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 23 endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 24 listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 25 statefulsession "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/stateful_session/v3" 26 cookiev3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/stateful_session/cookie/v3" 27 httpv3 "github.com/envoyproxy/go-control-plane/envoy/type/http/v3" 28 "github.com/google/go-cmp/cmp" 29 "google.golang.org/protobuf/testing/protocmp" 30 structpb "google.golang.org/protobuf/types/known/structpb" 31 wrappers "google.golang.org/protobuf/types/known/wrapperspb" 32 33 networking "istio.io/api/networking/v1alpha3" 34 "istio.io/istio/pilot/pkg/features" 35 "istio.io/istio/pilot/pkg/model" 36 "istio.io/istio/pilot/pkg/util/protoconv" 37 "istio.io/istio/pkg/config" 38 "istio.io/istio/pkg/config/labels" 39 "istio.io/istio/pkg/config/schema/gvk" 40 "istio.io/istio/pkg/test" 41 "istio.io/istio/pkg/test/util/assert" 42 xdsutil "istio.io/istio/pkg/wellknown" 43 ) 44 45 var testCla = &endpoint.ClusterLoadAssignment{ 46 ClusterName: "cluster", 47 Endpoints: []*endpoint.LocalityLbEndpoints{{ 48 Locality: &core.Locality{Region: "foo", Zone: "bar"}, 49 LbEndpoints: []*endpoint.LbEndpoint{ 50 { 51 HostIdentifier: &endpoint.LbEndpoint_Endpoint{Endpoint: &endpoint.Endpoint{Hostname: "foo", Address: BuildAddress("1.1.1.1", 80)}}, 52 LoadBalancingWeight: &wrappers.UInt32Value{Value: 100}, 53 }, 54 { 55 HostIdentifier: &endpoint.LbEndpoint_Endpoint{Endpoint: &endpoint.Endpoint{Hostname: "foo", Address: BuildAddress("1.1.1.1", 80)}}, 56 LoadBalancingWeight: &wrappers.UInt32Value{Value: 100}, 57 }, 58 }, 59 LoadBalancingWeight: &wrappers.UInt32Value{Value: 50}, 60 Priority: 2, 61 }}, 62 } 63 64 func BenchmarkCloneClusterLoadAssignment(b *testing.B) { 65 for i := 0; i < b.N; i++ { 66 cpy := CloneClusterLoadAssignment(testCla) 67 _ = cpy 68 } 69 } 70 71 func TestCloneClusterLoadAssignment(t *testing.T) { 72 cloned := CloneClusterLoadAssignment(testCla) 73 cloned2 := CloneClusterLoadAssignment(testCla) 74 if !cmp.Equal(testCla, cloned, protocmp.Transform()) { 75 t.Fatalf("expected %v to be the same as %v", testCla, cloned) 76 } 77 cloned.ClusterName = "foo" 78 cloned.Endpoints[0].LbEndpoints[0].LoadBalancingWeight.Value = 5 79 if cmp.Equal(testCla, cloned, protocmp.Transform()) { 80 t.Fatalf("expected %v to be the different from %v", testCla, cloned) 81 } 82 if !cmp.Equal(testCla, cloned2, protocmp.Transform()) { 83 t.Fatalf("expected %v to be the same as %v", testCla, cloned) 84 } 85 } 86 87 func TestConvertAddressToCidr(t *testing.T) { 88 tests := []struct { 89 name string 90 addr string 91 want *core.CidrRange 92 }{ 93 { 94 "return nil when the address is empty", 95 "", 96 nil, 97 }, 98 { 99 "cidr with two /", 100 "192.168.0.0//16", 101 nil, 102 }, 103 { 104 "cidr with invalid prefix length", 105 "192.168.0.0/ab", 106 nil, 107 }, 108 { 109 "cidr with negative prefix length", 110 "192.168.0.0/-16", 111 nil, 112 }, 113 { 114 "invalid ip address", 115 "19216800", 116 nil, 117 }, 118 { 119 "valid ipv6 address", 120 "2001:abcd:85a3::8a2e:370:1234", 121 &core.CidrRange{ 122 AddressPrefix: "2001:abcd:85a3::8a2e:370:1234", 123 PrefixLen: &wrappers.UInt32Value{Value: 128}, 124 }, 125 }, 126 { 127 "success case with no PrefixLen", 128 "1.2.3.4", 129 &core.CidrRange{ 130 AddressPrefix: "1.2.3.4", 131 PrefixLen: &wrappers.UInt32Value{ 132 Value: 32, 133 }, 134 }, 135 }, 136 { 137 "success case with PrefixLen", 138 "1.2.3.4/16", 139 &core.CidrRange{ 140 AddressPrefix: "1.2.3.4", 141 PrefixLen: &wrappers.UInt32Value{ 142 Value: 16, 143 }, 144 }, 145 }, 146 { 147 "ipv6", 148 "2001:db8::", 149 &core.CidrRange{ 150 AddressPrefix: "2001:db8::", 151 PrefixLen: &wrappers.UInt32Value{ 152 Value: 128, 153 }, 154 }, 155 }, 156 { 157 "ipv6 with prefix", 158 "2001:db8::/64", 159 &core.CidrRange{ 160 AddressPrefix: "2001:db8::", 161 PrefixLen: &wrappers.UInt32Value{ 162 Value: 64, 163 }, 164 }, 165 }, 166 } 167 for _, tt := range tests { 168 t.Run(tt.name, func(t *testing.T) { 169 if got := ConvertAddressToCidr(tt.addr); !reflect.DeepEqual(got, tt.want) { 170 t.Errorf("ConvertAddressToCidr() = %v, want %v", got, tt.want) 171 } 172 }) 173 } 174 } 175 176 func TestConvertLocality(t *testing.T) { 177 tests := []struct { 178 name string 179 locality string 180 want *core.Locality 181 reverse string 182 }{ 183 { 184 name: "nil locality", 185 locality: "", 186 want: &core.Locality{}, 187 }, 188 { 189 name: "locality with only region", 190 locality: "region", 191 want: &core.Locality{ 192 Region: "region", 193 }, 194 }, 195 { 196 name: "locality with region and zone", 197 locality: "region/zone", 198 want: &core.Locality{ 199 Region: "region", 200 Zone: "zone", 201 }, 202 }, 203 { 204 name: "locality with region zone and subzone", 205 locality: "region/zone/subzone", 206 want: &core.Locality{ 207 Region: "region", 208 Zone: "zone", 209 SubZone: "subzone", 210 }, 211 }, 212 { 213 name: "locality with region zone subzone and rack", 214 locality: "region/zone/subzone/rack", 215 want: &core.Locality{ 216 Region: "region", 217 Zone: "zone", 218 SubZone: "subzone", 219 }, 220 reverse: "region/zone/subzone", 221 }, 222 } 223 224 for _, tt := range tests { 225 t.Run(tt.name, func(t *testing.T) { 226 got := ConvertLocality(tt.locality) 227 if !reflect.DeepEqual(got, tt.want) { 228 t.Errorf("Expected locality %#v, but got %#v", tt.want, got) 229 } 230 // Verify we can reverse the conversion back to the original input 231 reverse := LocalityToString(got) 232 if tt.reverse != "" { 233 // Special case, reverse lookup is different than original input 234 if tt.reverse != reverse { 235 t.Errorf("Expected locality string %s, got %v", tt.reverse, reverse) 236 } 237 } else if tt.locality != reverse { 238 t.Errorf("Expected locality string %s, got %v", tt.locality, reverse) 239 } 240 }) 241 } 242 } 243 244 func TestLocalityMatch(t *testing.T) { 245 tests := []struct { 246 name string 247 locality *core.Locality 248 rule string 249 match bool 250 }{ 251 { 252 name: "wildcard matching", 253 locality: &core.Locality{ 254 Region: "region1", 255 Zone: "zone1", 256 SubZone: "subzone1", 257 }, 258 rule: "*", 259 match: true, 260 }, 261 { 262 name: "wildcard matching", 263 locality: &core.Locality{ 264 Region: "region1", 265 Zone: "zone1", 266 SubZone: "subzone1", 267 }, 268 rule: "region1/*", 269 match: true, 270 }, 271 { 272 name: "wildcard matching", 273 locality: &core.Locality{ 274 Region: "region1", 275 Zone: "zone1", 276 SubZone: "subzone1", 277 }, 278 rule: "region1/zone1/*", 279 match: true, 280 }, 281 { 282 name: "wildcard not matching", 283 locality: &core.Locality{ 284 Region: "region1", 285 Zone: "zone1", 286 SubZone: "subzone1", 287 }, 288 rule: "region1/zone2/*", 289 match: false, 290 }, 291 { 292 name: "region matching", 293 locality: &core.Locality{ 294 Region: "region1", 295 Zone: "zone1", 296 SubZone: "subzone1", 297 }, 298 rule: "region1", 299 match: true, 300 }, 301 { 302 name: "region and zone matching", 303 locality: &core.Locality{ 304 Region: "region1", 305 Zone: "zone1", 306 SubZone: "subzone1", 307 }, 308 rule: "region1/zone1", 309 match: true, 310 }, 311 { 312 name: "zubzone wildcard matching", 313 locality: &core.Locality{ 314 Region: "region1", 315 Zone: "zone1", 316 }, 317 rule: "region1/zone1", 318 match: true, 319 }, 320 { 321 name: "subzone mismatching", 322 locality: &core.Locality{ 323 Region: "region1", 324 Zone: "zone1", 325 }, 326 rule: "region1/zone1/subzone2", 327 match: false, 328 }, 329 } 330 331 for _, tt := range tests { 332 t.Run(tt.name, func(t *testing.T) { 333 match := LocalityMatch(tt.locality, tt.rule) 334 if match != tt.match { 335 t.Errorf("Expected matching result %v, but got %v", tt.match, match) 336 } 337 }) 338 } 339 } 340 341 func TestIsLocalityEmpty(t *testing.T) { 342 tests := []struct { 343 name string 344 locality *core.Locality 345 want bool 346 }{ 347 { 348 "non empty locality", 349 &core.Locality{ 350 Region: "region", 351 }, 352 false, 353 }, 354 { 355 "empty locality", 356 &core.Locality{ 357 Region: "", 358 }, 359 true, 360 }, 361 { 362 "nil locality", 363 nil, 364 true, 365 }, 366 } 367 368 for _, tt := range tests { 369 t.Run(tt.name, func(t *testing.T) { 370 got := IsLocalityEmpty(tt.locality) 371 if !reflect.DeepEqual(got, tt.want) { 372 t.Errorf("Expected locality empty result %#v, but got %#v", tt.want, got) 373 } 374 }) 375 } 376 } 377 378 func TestBuildConfigInfoMetadata(t *testing.T) { 379 cases := []struct { 380 name string 381 in config.Meta 382 want *core.Metadata 383 }{ 384 { 385 "destination-rule", 386 config.Meta{ 387 Name: "svcA", 388 Namespace: "default", 389 Domain: "svc.cluster.local", 390 GroupVersionKind: gvk.DestinationRule, 391 }, 392 &core.Metadata{ 393 FilterMetadata: map[string]*structpb.Struct{ 394 IstioMetadataKey: { 395 Fields: map[string]*structpb.Value{ 396 "config": { 397 Kind: &structpb.Value_StringValue{ 398 StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", 399 }, 400 }, 401 }, 402 }, 403 }, 404 }, 405 }, 406 } 407 408 for _, v := range cases { 409 t.Run(v.name, func(tt *testing.T) { 410 got := BuildConfigInfoMetadata(v.in) 411 if diff := cmp.Diff(got, v.want, protocmp.Transform()); diff != "" { 412 tt.Errorf("BuildConfigInfoMetadata(%v) produced incorrect result:\ngot: %v\nwant: %v\nDiff: %s", v.in, got, v.want, diff) 413 } 414 }) 415 } 416 } 417 418 func TestAddConfigInfoMetadata(t *testing.T) { 419 cases := []struct { 420 name string 421 in config.Meta 422 meta *core.Metadata 423 want *core.Metadata 424 }{ 425 { 426 "nil metadata", 427 config.Meta{ 428 Name: "svcA", 429 Namespace: "default", 430 Domain: "svc.cluster.local", 431 GroupVersionKind: gvk.DestinationRule, 432 }, 433 nil, 434 &core.Metadata{ 435 FilterMetadata: map[string]*structpb.Struct{ 436 IstioMetadataKey: { 437 Fields: map[string]*structpb.Value{ 438 "config": { 439 Kind: &structpb.Value_StringValue{ 440 StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", 441 }, 442 }, 443 }, 444 }, 445 }, 446 }, 447 }, 448 { 449 "empty metadata", 450 config.Meta{ 451 Name: "svcA", 452 Namespace: "default", 453 Domain: "svc.cluster.local", 454 GroupVersionKind: gvk.DestinationRule, 455 }, 456 &core.Metadata{ 457 FilterMetadata: map[string]*structpb.Struct{}, 458 }, 459 &core.Metadata{ 460 FilterMetadata: map[string]*structpb.Struct{ 461 IstioMetadataKey: { 462 Fields: map[string]*structpb.Value{ 463 "config": { 464 Kind: &structpb.Value_StringValue{ 465 StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", 466 }, 467 }, 468 }, 469 }, 470 }, 471 }, 472 }, 473 { 474 "existing istio metadata", 475 config.Meta{ 476 Name: "svcA", 477 Namespace: "default", 478 Domain: "svc.cluster.local", 479 GroupVersionKind: gvk.DestinationRule, 480 }, 481 &core.Metadata{ 482 FilterMetadata: map[string]*structpb.Struct{ 483 IstioMetadataKey: { 484 Fields: map[string]*structpb.Value{ 485 "other-config": { 486 Kind: &structpb.Value_StringValue{ 487 StringValue: "other-config", 488 }, 489 }, 490 }, 491 }, 492 }, 493 }, 494 &core.Metadata{ 495 FilterMetadata: map[string]*structpb.Struct{ 496 IstioMetadataKey: { 497 Fields: map[string]*structpb.Value{ 498 "other-config": { 499 Kind: &structpb.Value_StringValue{ 500 StringValue: "other-config", 501 }, 502 }, 503 "config": { 504 Kind: &structpb.Value_StringValue{ 505 StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", 506 }, 507 }, 508 }, 509 }, 510 }, 511 }, 512 }, 513 { 514 "existing non-istio metadata", 515 config.Meta{ 516 Name: "svcA", 517 Namespace: "default", 518 Domain: "svc.cluster.local", 519 GroupVersionKind: gvk.DestinationRule, 520 }, 521 &core.Metadata{ 522 FilterMetadata: map[string]*structpb.Struct{ 523 "other-metadata": { 524 Fields: map[string]*structpb.Value{ 525 "other-config": { 526 Kind: &structpb.Value_StringValue{ 527 StringValue: "other-config", 528 }, 529 }, 530 }, 531 }, 532 }, 533 }, 534 &core.Metadata{ 535 FilterMetadata: map[string]*structpb.Struct{ 536 "other-metadata": { 537 Fields: map[string]*structpb.Value{ 538 "other-config": { 539 Kind: &structpb.Value_StringValue{ 540 StringValue: "other-config", 541 }, 542 }, 543 }, 544 }, 545 IstioMetadataKey: { 546 Fields: map[string]*structpb.Value{ 547 "config": { 548 Kind: &structpb.Value_StringValue{ 549 StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", 550 }, 551 }, 552 }, 553 }, 554 }, 555 }, 556 }, 557 } 558 559 for _, v := range cases { 560 t.Run(v.name, func(tt *testing.T) { 561 got := AddConfigInfoMetadata(v.meta, v.in) 562 if diff := cmp.Diff(got, v.want, protocmp.Transform()); diff != "" { 563 tt.Errorf("AddConfigInfoMetadata(%v) produced incorrect result:\ngot: %v\nwant: %v\nDiff: %s", v.in, got, v.want, diff) 564 } 565 }) 566 } 567 } 568 569 func TestAddSubsetToMetadata(t *testing.T) { 570 cases := []struct { 571 name string 572 in *core.Metadata 573 subset string 574 want *core.Metadata 575 }{ 576 { 577 "simple subset", 578 &core.Metadata{ 579 FilterMetadata: map[string]*structpb.Struct{ 580 IstioMetadataKey: { 581 Fields: map[string]*structpb.Value{ 582 "config": { 583 Kind: &structpb.Value_StringValue{ 584 StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", 585 }, 586 }, 587 }, 588 }, 589 }, 590 }, 591 "test-subset", 592 &core.Metadata{ 593 FilterMetadata: map[string]*structpb.Struct{ 594 IstioMetadataKey: { 595 Fields: map[string]*structpb.Value{ 596 "config": { 597 Kind: &structpb.Value_StringValue{ 598 StringValue: "/apis/networking.istio.io/v1alpha3/namespaces/default/destination-rule/svcA", 599 }, 600 }, 601 "subset": { 602 Kind: &structpb.Value_StringValue{ 603 StringValue: "test-subset", 604 }, 605 }, 606 }, 607 }, 608 }, 609 }, 610 }, 611 { 612 "no metadata", 613 &core.Metadata{}, 614 "test-subset", 615 &core.Metadata{}, 616 }, 617 } 618 619 for _, v := range cases { 620 t.Run(v.name, func(tt *testing.T) { 621 AddSubsetToMetadata(v.in, v.subset) 622 got := v.in 623 if diff := cmp.Diff(got, v.want, protocmp.Transform()); diff != "" { 624 tt.Errorf("AddSubsetToMetadata(%v, %s) produced incorrect result:\ngot: %v\nwant: %v\nDiff: %s", v.in, v.subset, got, v.want, diff) 625 } 626 }) 627 } 628 } 629 630 func TestAddALPNOverrideToMetadata(t *testing.T) { 631 alpnOverrideFalse := &core.Metadata{ 632 FilterMetadata: map[string]*structpb.Struct{ 633 IstioMetadataKey: { 634 Fields: map[string]*structpb.Value{ 635 AlpnOverrideMetadataKey: { 636 Kind: &structpb.Value_StringValue{ 637 StringValue: "false", 638 }, 639 }, 640 }, 641 }, 642 }, 643 } 644 645 cases := []struct { 646 name string 647 tlsMode networking.ClientTLSSettings_TLSmode 648 meta *core.Metadata 649 want *core.Metadata 650 }{ 651 { 652 name: "ISTIO_MUTUAL TLS", 653 tlsMode: networking.ClientTLSSettings_ISTIO_MUTUAL, 654 meta: nil, 655 want: nil, 656 }, 657 { 658 name: "DISABLED TLS", 659 tlsMode: networking.ClientTLSSettings_DISABLE, 660 meta: nil, 661 want: nil, 662 }, 663 { 664 name: "SIMPLE TLS and nil metadata", 665 tlsMode: networking.ClientTLSSettings_SIMPLE, 666 meta: nil, 667 want: alpnOverrideFalse, 668 }, 669 { 670 name: "MUTUAL TLS and nil metadata", 671 tlsMode: networking.ClientTLSSettings_SIMPLE, 672 meta: nil, 673 want: alpnOverrideFalse, 674 }, 675 { 676 name: "SIMPLE TLS and empty metadata", 677 tlsMode: networking.ClientTLSSettings_SIMPLE, 678 meta: &core.Metadata{ 679 FilterMetadata: map[string]*structpb.Struct{}, 680 }, 681 want: alpnOverrideFalse, 682 }, 683 { 684 name: "SIMPLE TLS and existing istio metadata", 685 tlsMode: networking.ClientTLSSettings_SIMPLE, 686 meta: &core.Metadata{ 687 FilterMetadata: map[string]*structpb.Struct{ 688 IstioMetadataKey: { 689 Fields: map[string]*structpb.Value{ 690 "other-config": { 691 Kind: &structpb.Value_StringValue{ 692 StringValue: "other-config", 693 }, 694 }, 695 }, 696 }, 697 }, 698 }, 699 want: &core.Metadata{ 700 FilterMetadata: map[string]*structpb.Struct{ 701 IstioMetadataKey: { 702 Fields: map[string]*structpb.Value{ 703 "other-config": { 704 Kind: &structpb.Value_StringValue{ 705 StringValue: "other-config", 706 }, 707 }, 708 AlpnOverrideMetadataKey: { 709 Kind: &structpb.Value_StringValue{ 710 StringValue: "false", 711 }, 712 }, 713 }, 714 }, 715 }, 716 }, 717 }, 718 { 719 name: "SIMPLE TLS and existing non-istio metadata", 720 tlsMode: networking.ClientTLSSettings_SIMPLE, 721 meta: &core.Metadata{ 722 FilterMetadata: map[string]*structpb.Struct{ 723 "other-metadata": { 724 Fields: map[string]*structpb.Value{ 725 "other-config": { 726 Kind: &structpb.Value_StringValue{ 727 StringValue: "other-config", 728 }, 729 }, 730 }, 731 }, 732 }, 733 }, 734 want: &core.Metadata{ 735 FilterMetadata: map[string]*structpb.Struct{ 736 "other-metadata": { 737 Fields: map[string]*structpb.Value{ 738 "other-config": { 739 Kind: &structpb.Value_StringValue{ 740 StringValue: "other-config", 741 }, 742 }, 743 }, 744 }, 745 IstioMetadataKey: { 746 Fields: map[string]*structpb.Value{ 747 AlpnOverrideMetadataKey: { 748 Kind: &structpb.Value_StringValue{ 749 StringValue: "false", 750 }, 751 }, 752 }, 753 }, 754 }, 755 }, 756 }, 757 } 758 759 for _, v := range cases { 760 t.Run(v.name, func(tt *testing.T) { 761 got := AddALPNOverrideToMetadata(v.meta, v.tlsMode) 762 if diff := cmp.Diff(got, v.want, protocmp.Transform()); diff != "" { 763 tt.Errorf("AddALPNOverrideToMetadata produced incorrect result:\ngot: %v\nwant: %v\nDiff: %s", got, v.want, diff) 764 } 765 }) 766 } 767 } 768 769 func TestIsHTTPFilterChain(t *testing.T) { 770 httpFilterChain := &listener.FilterChain{ 771 Filters: []*listener.Filter{ 772 { 773 Name: xdsutil.HTTPConnectionManager, 774 }, 775 }, 776 } 777 778 tcpFilterChain := &listener.FilterChain{ 779 Filters: []*listener.Filter{ 780 { 781 Name: xdsutil.TCPProxy, 782 }, 783 }, 784 } 785 786 if !IsHTTPFilterChain(httpFilterChain) { 787 t.Errorf("http Filter chain not detected properly") 788 } 789 790 if IsHTTPFilterChain(tcpFilterChain) { 791 t.Errorf("tcp filter chain detected as http filter chain") 792 } 793 } 794 795 func TestIsAllowAnyOutbound(t *testing.T) { 796 tests := []struct { 797 name string 798 node *model.Proxy 799 result bool 800 }{ 801 { 802 name: "NilSidecarScope", 803 node: &model.Proxy{}, 804 result: false, 805 }, 806 { 807 name: "NilOutboundTrafficPolicy", 808 node: &model.Proxy{ 809 SidecarScope: &model.SidecarScope{}, 810 }, 811 result: false, 812 }, 813 { 814 name: "OutboundTrafficPolicyRegistryOnly", 815 node: &model.Proxy{ 816 SidecarScope: &model.SidecarScope{ 817 OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 818 Mode: networking.OutboundTrafficPolicy_REGISTRY_ONLY, 819 }, 820 }, 821 }, 822 result: false, 823 }, 824 { 825 name: "OutboundTrafficPolicyAllowAny", 826 node: &model.Proxy{ 827 SidecarScope: &model.SidecarScope{ 828 OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 829 Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, 830 }, 831 }, 832 }, 833 result: true, 834 }, 835 } 836 for i := range tests { 837 t.Run(tests[i].name, func(t *testing.T) { 838 out := IsAllowAnyOutbound(tests[i].node) 839 if out != tests[i].result { 840 t.Errorf("Expected %t but got %t for test case: %v\n", tests[i].result, out, tests[i].node) 841 } 842 }) 843 } 844 } 845 846 func TestBuildAddress(t *testing.T) { 847 testCases := []struct { 848 name string 849 addr string 850 port uint32 851 expected *core.Address 852 }{ 853 { 854 name: "ipv4", 855 addr: "172.10.10.1", 856 port: 8080, 857 expected: &core.Address{ 858 Address: &core.Address_SocketAddress{ 859 SocketAddress: &core.SocketAddress{ 860 Address: "172.10.10.1", 861 PortSpecifier: &core.SocketAddress_PortValue{ 862 PortValue: 8080, 863 }, 864 }, 865 }, 866 }, 867 }, 868 { 869 name: "ipv6", 870 addr: "fe80::10e7:52ff:fecd:198b", 871 port: 8080, 872 expected: &core.Address{ 873 Address: &core.Address_SocketAddress{ 874 SocketAddress: &core.SocketAddress{ 875 Address: "fe80::10e7:52ff:fecd:198b", 876 PortSpecifier: &core.SocketAddress_PortValue{ 877 PortValue: 8080, 878 }, 879 }, 880 }, 881 }, 882 }, 883 { 884 name: "uds", 885 addr: "/var/run/test/socket", 886 port: 0, 887 expected: &core.Address{ 888 Address: &core.Address_Pipe{ 889 Pipe: &core.Pipe{ 890 Path: "/var/run/test/socket", 891 }, 892 }, 893 }, 894 }, 895 { 896 name: "uds with unix prefix", 897 addr: "unix:///var/run/test/socket", 898 port: 0, 899 expected: &core.Address{ 900 Address: &core.Address_Pipe{ 901 Pipe: &core.Pipe{ 902 Path: "/var/run/test/socket", 903 }, 904 }, 905 }, 906 }, 907 } 908 909 for _, test := range testCases { 910 t.Run(test.name, func(t *testing.T) { 911 addr := BuildAddress(test.addr, test.port) 912 if !reflect.DeepEqual(addr, test.expected) { 913 t.Errorf("expected add %v, but got %v", test.expected, addr) 914 } 915 }) 916 } 917 } 918 919 func TestCidrRangeSliceEqual(t *testing.T) { 920 tests := []struct { 921 name string 922 first []*core.CidrRange 923 second []*core.CidrRange 924 want bool 925 }{ 926 { 927 "both nil", 928 nil, 929 nil, 930 true, 931 }, 932 { 933 "unequal length", 934 []*core.CidrRange{ 935 { 936 AddressPrefix: "1.2.3.4", 937 PrefixLen: &wrappers.UInt32Value{ 938 Value: 32, 939 }, 940 }, 941 { 942 AddressPrefix: "1.2.3.5", 943 PrefixLen: &wrappers.UInt32Value{ 944 Value: 32, 945 }, 946 }, 947 }, 948 []*core.CidrRange{ 949 { 950 AddressPrefix: "1.2.3.4", 951 PrefixLen: &wrappers.UInt32Value{ 952 Value: 32, 953 }, 954 }, 955 }, 956 false, 957 }, 958 { 959 "equal cidr", 960 []*core.CidrRange{ 961 { 962 AddressPrefix: "1.2.3.4", 963 PrefixLen: &wrappers.UInt32Value{ 964 Value: 32, 965 }, 966 }, 967 }, 968 []*core.CidrRange{ 969 { 970 AddressPrefix: "1.2.3.4", 971 PrefixLen: &wrappers.UInt32Value{ 972 Value: 32, 973 }, 974 }, 975 }, 976 true, 977 }, 978 { 979 "equal cidr with different insignificant bits", 980 []*core.CidrRange{ 981 { 982 AddressPrefix: "1.2.3.4", 983 PrefixLen: &wrappers.UInt32Value{ 984 Value: 24, 985 }, 986 }, 987 }, 988 []*core.CidrRange{ 989 { 990 AddressPrefix: "1.2.3.5", 991 PrefixLen: &wrappers.UInt32Value{ 992 Value: 24, 993 }, 994 }, 995 }, 996 true, 997 }, 998 { 999 "unequal cidr address prefix mismatch", 1000 []*core.CidrRange{ 1001 { 1002 AddressPrefix: "1.2.3.4", 1003 PrefixLen: &wrappers.UInt32Value{ 1004 Value: 32, 1005 }, 1006 }, 1007 }, 1008 []*core.CidrRange{ 1009 { 1010 AddressPrefix: "1.2.3.5", 1011 PrefixLen: &wrappers.UInt32Value{ 1012 Value: 32, 1013 }, 1014 }, 1015 }, 1016 false, 1017 }, 1018 { 1019 "unequal cidr prefixlen mismatch", 1020 []*core.CidrRange{ 1021 { 1022 AddressPrefix: "1.2.3.4", 1023 PrefixLen: &wrappers.UInt32Value{ 1024 Value: 32, 1025 }, 1026 }, 1027 }, 1028 []*core.CidrRange{ 1029 { 1030 AddressPrefix: "1.2.3.4", 1031 PrefixLen: &wrappers.UInt32Value{ 1032 Value: 16, 1033 }, 1034 }, 1035 }, 1036 false, 1037 }, 1038 } 1039 for _, tt := range tests { 1040 t.Run(tt.name, func(t *testing.T) { 1041 if got := CidrRangeSliceEqual(tt.first, tt.second); got != tt.want { 1042 t.Errorf("Unexpected CidrRangeSliceEqual() = %v, want %v", got, tt.want) 1043 } 1044 }) 1045 } 1046 } 1047 1048 func TestEndpointMetadata(t *testing.T) { 1049 test.SetForTest(t, &features.EndpointTelemetryLabel, true) 1050 cases := []struct { 1051 name string 1052 metadata *model.EndpointMetadata 1053 want *core.Metadata 1054 }{ 1055 { 1056 name: "all empty", 1057 metadata: &model.EndpointMetadata{ 1058 TLSMode: model.DisabledTLSModeLabel, 1059 Network: "", 1060 WorkloadName: "", 1061 ClusterID: "", 1062 }, 1063 want: &core.Metadata{ 1064 FilterMetadata: map[string]*structpb.Struct{ 1065 IstioMetadataKey: { 1066 Fields: map[string]*structpb.Value{ 1067 "workload": { 1068 Kind: &structpb.Value_StringValue{ 1069 StringValue: ";;;;", 1070 }, 1071 }, 1072 }, 1073 }, 1074 }, 1075 }, 1076 }, 1077 { 1078 name: "tls mode", 1079 metadata: &model.EndpointMetadata{ 1080 TLSMode: model.IstioMutualTLSModeLabel, 1081 Network: "", 1082 WorkloadName: "", 1083 ClusterID: "", 1084 }, 1085 want: &core.Metadata{ 1086 FilterMetadata: map[string]*structpb.Struct{ 1087 EnvoyTransportSocketMetadataKey: { 1088 Fields: map[string]*structpb.Value{ 1089 model.TLSModeLabelShortname: { 1090 Kind: &structpb.Value_StringValue{ 1091 StringValue: model.IstioMutualTLSModeLabel, 1092 }, 1093 }, 1094 }, 1095 }, 1096 IstioMetadataKey: { 1097 Fields: map[string]*structpb.Value{ 1098 "workload": { 1099 Kind: &structpb.Value_StringValue{ 1100 StringValue: ";;;;", 1101 }, 1102 }, 1103 }, 1104 }, 1105 }, 1106 }, 1107 }, 1108 { 1109 name: "network and tls mode", 1110 metadata: &model.EndpointMetadata{ 1111 TLSMode: model.IstioMutualTLSModeLabel, 1112 Network: "network", 1113 WorkloadName: "", 1114 ClusterID: "", 1115 }, 1116 want: &core.Metadata{ 1117 FilterMetadata: map[string]*structpb.Struct{ 1118 EnvoyTransportSocketMetadataKey: { 1119 Fields: map[string]*structpb.Value{ 1120 model.TLSModeLabelShortname: { 1121 Kind: &structpb.Value_StringValue{ 1122 StringValue: model.IstioMutualTLSModeLabel, 1123 }, 1124 }, 1125 }, 1126 }, 1127 IstioMetadataKey: { 1128 Fields: map[string]*structpb.Value{ 1129 "workload": { 1130 Kind: &structpb.Value_StringValue{ 1131 StringValue: ";;;;", 1132 }, 1133 }, 1134 }, 1135 }, 1136 }, 1137 }, 1138 }, 1139 { 1140 name: "all label", 1141 metadata: &model.EndpointMetadata{ 1142 TLSMode: model.IstioMutualTLSModeLabel, 1143 Network: "network", 1144 WorkloadName: "workload", 1145 ClusterID: "cluster", 1146 Namespace: "default", 1147 Labels: labels.Instance{ 1148 model.IstioCanonicalServiceLabelName: "service", 1149 model.IstioCanonicalServiceRevisionLabelName: "v1", 1150 }, 1151 }, 1152 want: &core.Metadata{ 1153 FilterMetadata: map[string]*structpb.Struct{ 1154 EnvoyTransportSocketMetadataKey: { 1155 Fields: map[string]*structpb.Value{ 1156 model.TLSModeLabelShortname: { 1157 Kind: &structpb.Value_StringValue{ 1158 StringValue: model.IstioMutualTLSModeLabel, 1159 }, 1160 }, 1161 }, 1162 }, 1163 IstioMetadataKey: { 1164 Fields: map[string]*structpb.Value{ 1165 "workload": { 1166 Kind: &structpb.Value_StringValue{ 1167 StringValue: "workload;default;service;v1;cluster", 1168 }, 1169 }, 1170 }, 1171 }, 1172 }, 1173 }, 1174 }, 1175 { 1176 name: "miss pod label", 1177 metadata: &model.EndpointMetadata{ 1178 TLSMode: model.IstioMutualTLSModeLabel, 1179 Network: "network", 1180 WorkloadName: "workload", 1181 ClusterID: "cluster", 1182 Namespace: "default", 1183 }, 1184 want: &core.Metadata{ 1185 FilterMetadata: map[string]*structpb.Struct{ 1186 EnvoyTransportSocketMetadataKey: { 1187 Fields: map[string]*structpb.Value{ 1188 model.TLSModeLabelShortname: { 1189 Kind: &structpb.Value_StringValue{ 1190 StringValue: model.IstioMutualTLSModeLabel, 1191 }, 1192 }, 1193 }, 1194 }, 1195 IstioMetadataKey: { 1196 Fields: map[string]*structpb.Value{ 1197 "workload": { 1198 Kind: &structpb.Value_StringValue{ 1199 StringValue: "workload;default;workload;;cluster", 1200 }, 1201 }, 1202 }, 1203 }, 1204 }, 1205 }, 1206 }, 1207 { 1208 name: "miss workload name", 1209 metadata: &model.EndpointMetadata{ 1210 TLSMode: model.IstioMutualTLSModeLabel, 1211 Network: "network", 1212 WorkloadName: "", 1213 ClusterID: "cluster", 1214 Namespace: "", 1215 }, 1216 want: &core.Metadata{ 1217 FilterMetadata: map[string]*structpb.Struct{ 1218 EnvoyTransportSocketMetadataKey: { 1219 Fields: map[string]*structpb.Value{ 1220 model.TLSModeLabelShortname: { 1221 Kind: &structpb.Value_StringValue{ 1222 StringValue: model.IstioMutualTLSModeLabel, 1223 }, 1224 }, 1225 }, 1226 }, 1227 IstioMetadataKey: { 1228 Fields: map[string]*structpb.Value{ 1229 "workload": { 1230 Kind: &structpb.Value_StringValue{ 1231 StringValue: ";;;;cluster", 1232 }, 1233 }, 1234 }, 1235 }, 1236 }, 1237 }, 1238 }, 1239 } 1240 for _, tt := range cases { 1241 t.Run(tt.name, func(t *testing.T) { 1242 input := &core.Metadata{} 1243 AppendLbEndpointMetadata(tt.metadata, input) 1244 if !reflect.DeepEqual(input, tt.want) { 1245 t.Errorf("Unexpected Endpoint metadata got %v, want %v", input, tt.want) 1246 } 1247 }) 1248 } 1249 } 1250 1251 func TestByteCount(t *testing.T) { 1252 cases := []struct { 1253 in int 1254 out string 1255 }{ 1256 {1, "1B"}, 1257 {1000, "1.0kB"}, 1258 {1_000_000, "1.0MB"}, 1259 {1_500_000, "1.5MB"}, 1260 } 1261 for _, tt := range cases { 1262 t.Run(fmt.Sprint(tt.in), func(t *testing.T) { 1263 if got := ByteCount(tt.in); got != tt.out { 1264 t.Fatalf("got %v wanted %v", got, tt.out) 1265 } 1266 }) 1267 } 1268 } 1269 1270 func TestIPv6Compliant(t *testing.T) { 1271 tests := []struct { 1272 host string 1273 match string 1274 }{ 1275 {"localhost", "localhost"}, 1276 {"127.0.0.1", "127.0.0.1"}, 1277 {"::1", "[::1]"}, 1278 {"2001:4860:0:2001::68", "[2001:4860:0:2001::68]"}, 1279 } 1280 for _, tt := range tests { 1281 t.Run(fmt.Sprint(tt.host), func(t *testing.T) { 1282 if got := IPv6Compliant(tt.host); got != tt.match { 1283 t.Fatalf("got %v wanted %v", got, tt.match) 1284 } 1285 }) 1286 } 1287 } 1288 1289 func TestDomainName(t *testing.T) { 1290 tests := []struct { 1291 host string 1292 port int 1293 match string 1294 }{ 1295 {"localhost", 3000, "localhost:3000"}, 1296 {"127.0.0.1", 3000, "127.0.0.1:3000"}, 1297 {"::1", 3000, "[::1]:3000"}, 1298 {"2001:4860:0:2001::68", 3000, "[2001:4860:0:2001::68]:3000"}, 1299 } 1300 for _, tt := range tests { 1301 t.Run(fmt.Sprint(tt.host), func(t *testing.T) { 1302 if got := DomainName(tt.host, tt.port); got != tt.match { 1303 t.Fatalf("got %v wanted %v", got, tt.match) 1304 } 1305 }) 1306 } 1307 } 1308 1309 func TestStatefulSessionFilterConfig(t *testing.T) { 1310 cases := []struct { 1311 name string 1312 service *model.Service 1313 expectedconfig *statefulsession.StatefulSession 1314 }{ 1315 { 1316 name: "nil service", 1317 expectedconfig: nil, 1318 }, 1319 { 1320 name: "service without cookie path", 1321 service: &model.Service{ 1322 Attributes: model.ServiceAttributes{ 1323 Labels: map[string]string{features.PersistentSessionLabel: "test-cookie"}, 1324 }, 1325 }, 1326 expectedconfig: &statefulsession.StatefulSession{ 1327 SessionState: &core.TypedExtensionConfig{ 1328 Name: "envoy.http.stateful_session.cookie", 1329 TypedConfig: protoconv.MessageToAny(&cookiev3.CookieBasedSessionState{ 1330 Cookie: &httpv3.Cookie{ 1331 Path: "/", 1332 Name: "test-cookie", 1333 }, 1334 }), 1335 }, 1336 }, 1337 }, 1338 { 1339 name: "service with cookie path", 1340 service: &model.Service{ 1341 Attributes: model.ServiceAttributes{ 1342 Labels: map[string]string{features.PersistentSessionLabel: "test-cookie:/path"}, 1343 }, 1344 }, 1345 expectedconfig: &statefulsession.StatefulSession{ 1346 SessionState: &core.TypedExtensionConfig{ 1347 Name: "envoy.http.stateful_session.cookie", 1348 TypedConfig: protoconv.MessageToAny(&cookiev3.CookieBasedSessionState{ 1349 Cookie: &httpv3.Cookie{ 1350 Path: "/path", 1351 Name: "test-cookie", 1352 }, 1353 }), 1354 }, 1355 }, 1356 }, 1357 } 1358 for _, tt := range cases { 1359 t.Run(tt.name, func(t *testing.T) { 1360 sessionConfig := MaybeBuildStatefulSessionFilterConfig(tt.service) 1361 if !reflect.DeepEqual(tt.expectedconfig, sessionConfig) { 1362 t.Errorf("unexpected stateful session filter config, expected: %v, got :%v", tt.expectedconfig, sessionConfig) 1363 } 1364 }) 1365 } 1366 } 1367 1368 func TestMergeSubsetTrafficPolicy(t *testing.T) { 1369 cases := []struct { 1370 name string 1371 original *networking.TrafficPolicy 1372 subset *networking.TrafficPolicy 1373 port *model.Port 1374 expected *networking.TrafficPolicy 1375 }{ 1376 { 1377 name: "all nil policies", 1378 original: nil, 1379 subset: nil, 1380 port: nil, 1381 expected: nil, 1382 }, 1383 { 1384 name: "no subset policy", 1385 original: &networking.TrafficPolicy{ 1386 ConnectionPool: &networking.ConnectionPoolSettings{ 1387 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1388 MaxRetries: 10, 1389 }, 1390 }, 1391 }, 1392 subset: nil, 1393 port: nil, 1394 expected: &networking.TrafficPolicy{ 1395 ConnectionPool: &networking.ConnectionPoolSettings{ 1396 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1397 MaxRetries: 10, 1398 }, 1399 }, 1400 }, 1401 }, 1402 { 1403 name: "no parent policy", 1404 original: nil, 1405 subset: &networking.TrafficPolicy{ 1406 ConnectionPool: &networking.ConnectionPoolSettings{ 1407 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1408 MaxRetries: 10, 1409 }, 1410 }, 1411 }, 1412 port: nil, 1413 expected: &networking.TrafficPolicy{ 1414 ConnectionPool: &networking.ConnectionPoolSettings{ 1415 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1416 MaxRetries: 10, 1417 }, 1418 }, 1419 }, 1420 }, 1421 { 1422 name: "merge non-conflicting fields", 1423 original: &networking.TrafficPolicy{ 1424 Tls: &networking.ClientTLSSettings{ 1425 Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, 1426 }, 1427 }, 1428 subset: &networking.TrafficPolicy{ 1429 ConnectionPool: &networking.ConnectionPoolSettings{ 1430 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1431 MaxRetries: 10, 1432 }, 1433 }, 1434 Tunnel: &networking.TrafficPolicy_TunnelSettings{ 1435 TargetHost: "example.com", 1436 TargetPort: 8443, 1437 }, 1438 }, 1439 port: nil, 1440 expected: &networking.TrafficPolicy{ 1441 ConnectionPool: &networking.ConnectionPoolSettings{ 1442 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1443 MaxRetries: 10, 1444 }, 1445 }, 1446 Tls: &networking.ClientTLSSettings{ 1447 Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, 1448 }, 1449 Tunnel: &networking.TrafficPolicy_TunnelSettings{ 1450 TargetHost: "example.com", 1451 TargetPort: 8443, 1452 }, 1453 }, 1454 }, 1455 { 1456 name: "subset overwrite top-level fields", 1457 original: &networking.TrafficPolicy{ 1458 Tls: &networking.ClientTLSSettings{ 1459 Mode: networking.ClientTLSSettings_ISTIO_MUTUAL, 1460 }, 1461 ConnectionPool: &networking.ConnectionPoolSettings{ 1462 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1463 MaxRetries: 10, 1464 }, 1465 }, 1466 }, 1467 subset: &networking.TrafficPolicy{ 1468 Tls: &networking.ClientTLSSettings{ 1469 Mode: networking.ClientTLSSettings_SIMPLE, 1470 }, 1471 }, 1472 port: nil, 1473 expected: &networking.TrafficPolicy{ 1474 Tls: &networking.ClientTLSSettings{ 1475 Mode: networking.ClientTLSSettings_SIMPLE, 1476 }, 1477 ConnectionPool: &networking.ConnectionPoolSettings{ 1478 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1479 MaxRetries: 10, 1480 }, 1481 }, 1482 }, 1483 }, 1484 { 1485 name: "merge port level policy, and do not inherit top-level fields", 1486 original: nil, 1487 subset: &networking.TrafficPolicy{ 1488 LoadBalancer: &networking.LoadBalancerSettings{ 1489 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1490 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 1491 }, 1492 }, 1493 ConnectionPool: &networking.ConnectionPoolSettings{ 1494 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1495 MaxRetries: 10, 1496 }, 1497 }, 1498 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1499 { 1500 Port: &networking.PortSelector{ 1501 Number: 8080, 1502 }, 1503 LoadBalancer: &networking.LoadBalancerSettings{ 1504 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1505 Simple: networking.LoadBalancerSettings_LEAST_REQUEST, 1506 }, 1507 }, 1508 }, 1509 }, 1510 }, 1511 port: &model.Port{Port: 8080}, 1512 expected: &networking.TrafficPolicy{ 1513 LoadBalancer: &networking.LoadBalancerSettings{ 1514 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1515 Simple: networking.LoadBalancerSettings_LEAST_REQUEST, 1516 }, 1517 }, 1518 }, 1519 }, 1520 { 1521 name: "merge port level policy, and do not inherit top-level fields", 1522 original: &networking.TrafficPolicy{ 1523 LoadBalancer: &networking.LoadBalancerSettings{ 1524 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1525 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 1526 }, 1527 }, 1528 OutlierDetection: &networking.OutlierDetection{ 1529 ConsecutiveErrors: 20, 1530 }, 1531 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1532 { 1533 Port: &networking.PortSelector{ 1534 Number: 8080, 1535 }, 1536 OutlierDetection: &networking.OutlierDetection{ 1537 ConsecutiveErrors: 15, 1538 }, 1539 }, 1540 }, 1541 }, 1542 subset: &networking.TrafficPolicy{ 1543 LoadBalancer: &networking.LoadBalancerSettings{ 1544 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1545 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 1546 }, 1547 }, 1548 ConnectionPool: &networking.ConnectionPoolSettings{ 1549 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1550 MaxRetries: 10, 1551 }, 1552 }, 1553 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1554 { 1555 Port: &networking.PortSelector{ 1556 Number: 8080, 1557 }, 1558 OutlierDetection: &networking.OutlierDetection{ 1559 ConsecutiveErrors: 13, 1560 }, 1561 }, 1562 }, 1563 }, 1564 port: &model.Port{Port: 8080}, 1565 expected: &networking.TrafficPolicy{ 1566 OutlierDetection: &networking.OutlierDetection{ 1567 ConsecutiveErrors: 13, 1568 }, 1569 }, 1570 }, 1571 { 1572 name: "default cluster, non-matching port selector", 1573 original: &networking.TrafficPolicy{ 1574 LoadBalancer: &networking.LoadBalancerSettings{ 1575 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1576 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 1577 }, 1578 }, 1579 OutlierDetection: &networking.OutlierDetection{ 1580 ConsecutiveErrors: 20, 1581 }, 1582 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1583 { 1584 Port: &networking.PortSelector{ 1585 Number: 8080, 1586 }, 1587 OutlierDetection: &networking.OutlierDetection{ 1588 ConsecutiveErrors: 15, 1589 }, 1590 }, 1591 }, 1592 }, 1593 subset: &networking.TrafficPolicy{ 1594 LoadBalancer: &networking.LoadBalancerSettings{ 1595 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1596 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 1597 }, 1598 }, 1599 ConnectionPool: &networking.ConnectionPoolSettings{ 1600 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1601 MaxRetries: 10, 1602 }, 1603 }, 1604 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 1605 { 1606 Port: &networking.PortSelector{ 1607 Number: 8080, 1608 }, 1609 OutlierDetection: &networking.OutlierDetection{ 1610 ConsecutiveErrors: 13, 1611 }, 1612 }, 1613 }, 1614 }, 1615 port: &model.Port{Port: 9090}, 1616 expected: &networking.TrafficPolicy{ 1617 LoadBalancer: &networking.LoadBalancerSettings{ 1618 LbPolicy: &networking.LoadBalancerSettings_Simple{ 1619 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 1620 }, 1621 }, 1622 ConnectionPool: &networking.ConnectionPoolSettings{ 1623 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 1624 MaxRetries: 10, 1625 }, 1626 }, 1627 OutlierDetection: &networking.OutlierDetection{ 1628 ConsecutiveErrors: 20, 1629 }, 1630 }, 1631 }, 1632 } 1633 1634 for _, tt := range cases { 1635 t.Run(tt.name, func(t *testing.T) { 1636 policy := MergeSubsetTrafficPolicy(tt.original, tt.subset, tt.port) 1637 assert.Equal(t, policy, tt.expected) 1638 }) 1639 } 1640 }