google.golang.org/grpc@v1.62.1/xds/internal/balancer/clusterresolver/configbuilder_test.go (about) 1 /* 2 * 3 * Copyright 2021 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package clusterresolver 20 21 import ( 22 "bytes" 23 "encoding/json" 24 "fmt" 25 "sort" 26 "testing" 27 "time" 28 29 "github.com/google/go-cmp/cmp" 30 "google.golang.org/grpc/attributes" 31 "google.golang.org/grpc/balancer" 32 "google.golang.org/grpc/balancer/roundrobin" 33 "google.golang.org/grpc/balancer/weightedroundrobin" 34 "google.golang.org/grpc/internal/hierarchy" 35 iserviceconfig "google.golang.org/grpc/internal/serviceconfig" 36 "google.golang.org/grpc/resolver" 37 "google.golang.org/grpc/xds/internal" 38 "google.golang.org/grpc/xds/internal/balancer/clusterimpl" 39 "google.golang.org/grpc/xds/internal/balancer/outlierdetection" 40 "google.golang.org/grpc/xds/internal/balancer/priority" 41 "google.golang.org/grpc/xds/internal/balancer/ringhash" 42 "google.golang.org/grpc/xds/internal/balancer/wrrlocality" 43 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource" 44 ) 45 46 const ( 47 testLRSServer = "test-lrs-server" 48 testMaxRequests = 314 49 testEDSServiceName = "service-name-from-parent" 50 testDropCategory = "test-drops" 51 testDropOverMillion = 1 52 53 localityCount = 5 54 addressPerLocality = 2 55 ) 56 57 var ( 58 testLocalityIDs []internal.LocalityID 59 testAddressStrs [][]string 60 testEndpoints [][]xdsresource.Endpoint 61 62 testLocalitiesP0, testLocalitiesP1 []xdsresource.Locality 63 64 addrCmpOpts = cmp.Options{ 65 cmp.AllowUnexported(attributes.Attributes{}), 66 cmp.Transformer("SortAddrs", func(in []resolver.Address) []resolver.Address { 67 out := append([]resolver.Address(nil), in...) // Copy input to avoid mutating it 68 sort.Slice(out, func(i, j int) bool { 69 return out[i].Addr < out[j].Addr 70 }) 71 return out 72 }), 73 } 74 75 noopODCfg = outlierdetection.LBConfig{ 76 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 77 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 78 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 79 MaxEjectionPercent: 10, 80 } 81 ) 82 83 func init() { 84 for i := 0; i < localityCount; i++ { 85 testLocalityIDs = append(testLocalityIDs, internal.LocalityID{Zone: fmt.Sprintf("test-zone-%d", i)}) 86 var ( 87 addrs []string 88 ends []xdsresource.Endpoint 89 ) 90 for j := 0; j < addressPerLocality; j++ { 91 addr := fmt.Sprintf("addr-%d-%d", i, j) 92 addrs = append(addrs, addr) 93 ends = append(ends, xdsresource.Endpoint{ 94 Address: addr, 95 HealthStatus: xdsresource.EndpointHealthStatusHealthy, 96 }) 97 } 98 testAddressStrs = append(testAddressStrs, addrs) 99 testEndpoints = append(testEndpoints, ends) 100 } 101 102 testLocalitiesP0 = []xdsresource.Locality{ 103 { 104 Endpoints: testEndpoints[0], 105 ID: testLocalityIDs[0], 106 Weight: 20, 107 Priority: 0, 108 }, 109 { 110 Endpoints: testEndpoints[1], 111 ID: testLocalityIDs[1], 112 Weight: 80, 113 Priority: 0, 114 }, 115 } 116 testLocalitiesP1 = []xdsresource.Locality{ 117 { 118 Endpoints: testEndpoints[2], 119 ID: testLocalityIDs[2], 120 Weight: 20, 121 Priority: 1, 122 }, 123 { 124 Endpoints: testEndpoints[3], 125 ID: testLocalityIDs[3], 126 Weight: 80, 127 Priority: 1, 128 }, 129 } 130 } 131 132 // TestBuildPriorityConfigJSON is a sanity check that the built balancer config 133 // can be parsed. The behavior test is covered by TestBuildPriorityConfig. 134 func TestBuildPriorityConfigJSON(t *testing.T) { 135 gotConfig, _, err := buildPriorityConfigJSON([]priorityConfig{ 136 { 137 mechanism: DiscoveryMechanism{ 138 Cluster: testClusterName, 139 LoadReportingServer: testLRSServerConfig, 140 MaxConcurrentRequests: newUint32(testMaxRequests), 141 Type: DiscoveryMechanismTypeEDS, 142 EDSServiceName: testEDSServiceName, 143 }, 144 edsResp: xdsresource.EndpointsUpdate{ 145 Drops: []xdsresource.OverloadDropConfig{ 146 { 147 Category: testDropCategory, 148 Numerator: testDropOverMillion, 149 Denominator: million, 150 }, 151 }, 152 Localities: []xdsresource.Locality{ 153 testLocalitiesP0[0], 154 testLocalitiesP0[1], 155 testLocalitiesP1[0], 156 testLocalitiesP1[1], 157 }, 158 }, 159 childNameGen: newNameGenerator(0), 160 }, 161 { 162 mechanism: DiscoveryMechanism{ 163 Type: DiscoveryMechanismTypeLogicalDNS, 164 }, 165 addresses: testAddressStrs[4], 166 childNameGen: newNameGenerator(1), 167 }, 168 }, nil) 169 if err != nil { 170 t.Fatalf("buildPriorityConfigJSON(...) failed: %v", err) 171 } 172 173 var prettyGot bytes.Buffer 174 if err := json.Indent(&prettyGot, gotConfig, ">>> ", " "); err != nil { 175 t.Fatalf("json.Indent() failed: %v", err) 176 } 177 // Print the indented json if this test fails. 178 t.Log(prettyGot.String()) 179 180 priorityB := balancer.Get(priority.Name) 181 if _, err = priorityB.(balancer.ConfigParser).ParseConfig(gotConfig); err != nil { 182 t.Fatalf("ParseConfig(%+v) failed: %v", gotConfig, err) 183 } 184 } 185 186 // TestBuildPriorityConfig tests the priority config generation. Each top level 187 // balancer per priority should be an Outlier Detection balancer, with a Cluster 188 // Impl Balancer as a child. 189 func TestBuildPriorityConfig(t *testing.T) { 190 gotConfig, _, _ := buildPriorityConfig([]priorityConfig{ 191 { 192 // EDS - OD config should be the top level for both of the EDS 193 // priorities balancer This EDS priority will have multiple sub 194 // priorities. The Outlier Detection configuration specified in the 195 // Discovery Mechanism should be the top level for each sub 196 // priorities balancer. 197 mechanism: DiscoveryMechanism{ 198 Cluster: testClusterName, 199 Type: DiscoveryMechanismTypeEDS, 200 EDSServiceName: testEDSServiceName, 201 outlierDetection: noopODCfg, 202 }, 203 edsResp: xdsresource.EndpointsUpdate{ 204 Localities: []xdsresource.Locality{ 205 testLocalitiesP0[0], 206 testLocalitiesP0[1], 207 testLocalitiesP1[0], 208 testLocalitiesP1[1], 209 }, 210 }, 211 childNameGen: newNameGenerator(0), 212 }, 213 { 214 // This OD config should wrap the Logical DNS priorities balancer. 215 mechanism: DiscoveryMechanism{ 216 Cluster: testClusterName2, 217 Type: DiscoveryMechanismTypeLogicalDNS, 218 outlierDetection: noopODCfg, 219 }, 220 addresses: testAddressStrs[4], 221 childNameGen: newNameGenerator(1), 222 }, 223 }, nil) 224 225 wantConfig := &priority.LBConfig{ 226 Children: map[string]*priority.Child{ 227 "priority-0-0": { 228 Config: &iserviceconfig.BalancerConfig{ 229 Name: outlierdetection.Name, 230 Config: &outlierdetection.LBConfig{ 231 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 232 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 233 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 234 MaxEjectionPercent: 10, 235 ChildPolicy: &iserviceconfig.BalancerConfig{ 236 Name: clusterimpl.Name, 237 Config: &clusterimpl.LBConfig{ 238 Cluster: testClusterName, 239 EDSServiceName: testEDSServiceName, 240 DropCategories: []clusterimpl.DropConfig{}, 241 }, 242 }, 243 }, 244 }, 245 IgnoreReresolutionRequests: true, 246 }, 247 "priority-0-1": { 248 Config: &iserviceconfig.BalancerConfig{ 249 Name: outlierdetection.Name, 250 Config: &outlierdetection.LBConfig{ 251 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 252 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 253 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 254 MaxEjectionPercent: 10, 255 ChildPolicy: &iserviceconfig.BalancerConfig{ 256 Name: clusterimpl.Name, 257 Config: &clusterimpl.LBConfig{ 258 Cluster: testClusterName, 259 EDSServiceName: testEDSServiceName, 260 DropCategories: []clusterimpl.DropConfig{}, 261 }, 262 }, 263 }, 264 }, 265 IgnoreReresolutionRequests: true, 266 }, 267 "priority-1": { 268 Config: &iserviceconfig.BalancerConfig{ 269 Name: outlierdetection.Name, 270 Config: &outlierdetection.LBConfig{ 271 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 272 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 273 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 274 MaxEjectionPercent: 10, 275 ChildPolicy: &iserviceconfig.BalancerConfig{ 276 Name: clusterimpl.Name, 277 Config: &clusterimpl.LBConfig{ 278 Cluster: testClusterName2, 279 ChildPolicy: &iserviceconfig.BalancerConfig{Name: "pick_first"}, 280 }, 281 }, 282 }, 283 }, 284 IgnoreReresolutionRequests: false, 285 }, 286 }, 287 Priorities: []string{"priority-0-0", "priority-0-1", "priority-1"}, 288 } 289 if diff := cmp.Diff(gotConfig, wantConfig); diff != "" { 290 t.Errorf("buildPriorityConfig() diff (-got +want) %v", diff) 291 } 292 } 293 294 func TestBuildClusterImplConfigForDNS(t *testing.T) { 295 gotName, gotConfig, gotAddrs := buildClusterImplConfigForDNS(newNameGenerator(3), testAddressStrs[0], DiscoveryMechanism{Cluster: testClusterName2, Type: DiscoveryMechanismTypeLogicalDNS}) 296 wantName := "priority-3" 297 wantConfig := &clusterimpl.LBConfig{ 298 Cluster: testClusterName2, 299 ChildPolicy: &iserviceconfig.BalancerConfig{ 300 Name: "pick_first", 301 }, 302 } 303 wantAddrs := []resolver.Address{ 304 hierarchy.Set(resolver.Address{Addr: testAddressStrs[0][0]}, []string{"priority-3"}), 305 hierarchy.Set(resolver.Address{Addr: testAddressStrs[0][1]}, []string{"priority-3"}), 306 } 307 308 if diff := cmp.Diff(gotName, wantName); diff != "" { 309 t.Errorf("buildClusterImplConfigForDNS() diff (-got +want) %v", diff) 310 } 311 if diff := cmp.Diff(gotConfig, wantConfig); diff != "" { 312 t.Errorf("buildClusterImplConfigForDNS() diff (-got +want) %v", diff) 313 } 314 if diff := cmp.Diff(gotAddrs, wantAddrs, addrCmpOpts); diff != "" { 315 t.Errorf("buildClusterImplConfigForDNS() diff (-got +want) %v", diff) 316 } 317 } 318 319 func TestBuildClusterImplConfigForEDS(t *testing.T) { 320 gotNames, gotConfigs, gotAddrs, _ := buildClusterImplConfigForEDS( 321 newNameGenerator(2), 322 xdsresource.EndpointsUpdate{ 323 Drops: []xdsresource.OverloadDropConfig{ 324 { 325 Category: testDropCategory, 326 Numerator: testDropOverMillion, 327 Denominator: million, 328 }, 329 }, 330 Localities: []xdsresource.Locality{ 331 { 332 Endpoints: testEndpoints[3], 333 ID: testLocalityIDs[3], 334 Weight: 80, 335 Priority: 1, 336 }, { 337 Endpoints: testEndpoints[1], 338 ID: testLocalityIDs[1], 339 Weight: 80, 340 Priority: 0, 341 }, { 342 Endpoints: testEndpoints[2], 343 ID: testLocalityIDs[2], 344 Weight: 20, 345 Priority: 1, 346 }, { 347 Endpoints: testEndpoints[0], 348 ID: testLocalityIDs[0], 349 Weight: 20, 350 Priority: 0, 351 }, 352 }, 353 }, 354 DiscoveryMechanism{ 355 Cluster: testClusterName, 356 MaxConcurrentRequests: newUint32(testMaxRequests), 357 LoadReportingServer: testLRSServerConfig, 358 Type: DiscoveryMechanismTypeEDS, 359 EDSServiceName: testEDSServiceName, 360 }, 361 nil, 362 ) 363 364 wantNames := []string{ 365 fmt.Sprintf("priority-%v-%v", 2, 0), 366 fmt.Sprintf("priority-%v-%v", 2, 1), 367 } 368 wantConfigs := map[string]*clusterimpl.LBConfig{ 369 "priority-2-0": { 370 Cluster: testClusterName, 371 EDSServiceName: testEDSServiceName, 372 LoadReportingServer: testLRSServerConfig, 373 MaxConcurrentRequests: newUint32(testMaxRequests), 374 DropCategories: []clusterimpl.DropConfig{ 375 { 376 Category: testDropCategory, 377 RequestsPerMillion: testDropOverMillion, 378 }, 379 }, 380 }, 381 "priority-2-1": { 382 Cluster: testClusterName, 383 EDSServiceName: testEDSServiceName, 384 LoadReportingServer: testLRSServerConfig, 385 MaxConcurrentRequests: newUint32(testMaxRequests), 386 DropCategories: []clusterimpl.DropConfig{ 387 { 388 Category: testDropCategory, 389 RequestsPerMillion: testDropOverMillion, 390 }, 391 }, 392 }, 393 } 394 wantAddrs := []resolver.Address{ 395 testAddrWithAttrs(testAddressStrs[0][0], 20, 1, "priority-2-0", &testLocalityIDs[0]), 396 testAddrWithAttrs(testAddressStrs[0][1], 20, 1, "priority-2-0", &testLocalityIDs[0]), 397 testAddrWithAttrs(testAddressStrs[1][0], 80, 1, "priority-2-0", &testLocalityIDs[1]), 398 testAddrWithAttrs(testAddressStrs[1][1], 80, 1, "priority-2-0", &testLocalityIDs[1]), 399 testAddrWithAttrs(testAddressStrs[2][0], 20, 1, "priority-2-1", &testLocalityIDs[2]), 400 testAddrWithAttrs(testAddressStrs[2][1], 20, 1, "priority-2-1", &testLocalityIDs[2]), 401 testAddrWithAttrs(testAddressStrs[3][0], 80, 1, "priority-2-1", &testLocalityIDs[3]), 402 testAddrWithAttrs(testAddressStrs[3][1], 80, 1, "priority-2-1", &testLocalityIDs[3]), 403 } 404 405 if diff := cmp.Diff(gotNames, wantNames); diff != "" { 406 t.Errorf("buildClusterImplConfigForEDS() diff (-got +want) %v", diff) 407 } 408 if diff := cmp.Diff(gotConfigs, wantConfigs); diff != "" { 409 t.Errorf("buildClusterImplConfigForEDS() diff (-got +want) %v", diff) 410 } 411 if diff := cmp.Diff(gotAddrs, wantAddrs, addrCmpOpts); diff != "" { 412 t.Errorf("buildClusterImplConfigForEDS() diff (-got +want) %v", diff) 413 } 414 415 } 416 417 func TestGroupLocalitiesByPriority(t *testing.T) { 418 tests := []struct { 419 name string 420 localities []xdsresource.Locality 421 wantLocalities [][]xdsresource.Locality 422 }{ 423 { 424 name: "1 locality 1 priority", 425 localities: []xdsresource.Locality{testLocalitiesP0[0]}, 426 wantLocalities: [][]xdsresource.Locality{ 427 {testLocalitiesP0[0]}, 428 }, 429 }, 430 { 431 name: "2 locality 1 priority", 432 localities: []xdsresource.Locality{testLocalitiesP0[0], testLocalitiesP0[1]}, 433 wantLocalities: [][]xdsresource.Locality{ 434 {testLocalitiesP0[0], testLocalitiesP0[1]}, 435 }, 436 }, 437 { 438 name: "1 locality in each", 439 localities: []xdsresource.Locality{testLocalitiesP0[0], testLocalitiesP1[0]}, 440 wantLocalities: [][]xdsresource.Locality{ 441 {testLocalitiesP0[0]}, 442 {testLocalitiesP1[0]}, 443 }, 444 }, 445 { 446 name: "2 localities in each sorted", 447 localities: []xdsresource.Locality{ 448 testLocalitiesP0[0], testLocalitiesP0[1], 449 testLocalitiesP1[0], testLocalitiesP1[1]}, 450 wantLocalities: [][]xdsresource.Locality{ 451 {testLocalitiesP0[0], testLocalitiesP0[1]}, 452 {testLocalitiesP1[0], testLocalitiesP1[1]}, 453 }, 454 }, 455 { 456 // The localities are given in order [p1, p0, p1, p0], but the 457 // returned priority list must be sorted [p0, p1], because the list 458 // order is the priority order. 459 name: "2 localities in each needs to sort", 460 localities: []xdsresource.Locality{ 461 testLocalitiesP1[1], testLocalitiesP0[1], 462 testLocalitiesP1[0], testLocalitiesP0[0]}, 463 wantLocalities: [][]xdsresource.Locality{ 464 {testLocalitiesP0[1], testLocalitiesP0[0]}, 465 {testLocalitiesP1[1], testLocalitiesP1[0]}, 466 }, 467 }, 468 } 469 for _, tt := range tests { 470 t.Run(tt.name, func(t *testing.T) { 471 gotLocalities := groupLocalitiesByPriority(tt.localities) 472 if diff := cmp.Diff(gotLocalities, tt.wantLocalities); diff != "" { 473 t.Errorf("groupLocalitiesByPriority() diff(-got +want) %v", diff) 474 } 475 }) 476 } 477 } 478 479 func TestDedupSortedIntSlice(t *testing.T) { 480 tests := []struct { 481 name string 482 a []int 483 want []int 484 }{ 485 { 486 name: "empty", 487 a: []int{}, 488 want: []int{}, 489 }, 490 { 491 name: "no dup", 492 a: []int{0, 1, 2, 3}, 493 want: []int{0, 1, 2, 3}, 494 }, 495 { 496 name: "with dup", 497 a: []int{0, 0, 1, 1, 1, 2, 3}, 498 want: []int{0, 1, 2, 3}, 499 }, 500 } 501 for _, tt := range tests { 502 t.Run(tt.name, func(t *testing.T) { 503 if got := dedupSortedIntSlice(tt.a); !cmp.Equal(got, tt.want) { 504 t.Errorf("dedupSortedIntSlice() = %v, want %v, diff %v", got, tt.want, cmp.Diff(got, tt.want)) 505 } 506 }) 507 } 508 } 509 510 func TestPriorityLocalitiesToClusterImpl(t *testing.T) { 511 tests := []struct { 512 name string 513 localities []xdsresource.Locality 514 priorityName string 515 mechanism DiscoveryMechanism 516 childPolicy *iserviceconfig.BalancerConfig 517 wantConfig *clusterimpl.LBConfig 518 wantAddrs []resolver.Address 519 wantErr bool 520 }{{ 521 name: "round robin as child, no LRS", 522 localities: []xdsresource.Locality{ 523 { 524 Endpoints: []xdsresource.Endpoint{ 525 {Address: "addr-1-1", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 90}, 526 {Address: "addr-1-2", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 10}, 527 }, 528 ID: internal.LocalityID{Zone: "test-zone-1"}, 529 Weight: 20, 530 }, 531 { 532 Endpoints: []xdsresource.Endpoint{ 533 {Address: "addr-2-1", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 90}, 534 {Address: "addr-2-2", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 10}, 535 }, 536 ID: internal.LocalityID{Zone: "test-zone-2"}, 537 Weight: 80, 538 }, 539 }, 540 priorityName: "test-priority", 541 childPolicy: &iserviceconfig.BalancerConfig{Name: roundrobin.Name}, 542 mechanism: DiscoveryMechanism{ 543 Cluster: testClusterName, 544 Type: DiscoveryMechanismTypeEDS, 545 EDSServiceName: testEDSService, 546 }, 547 // lrsServer is nil, so LRS policy will not be used. 548 wantConfig: &clusterimpl.LBConfig{ 549 Cluster: testClusterName, 550 EDSServiceName: testEDSService, 551 ChildPolicy: &iserviceconfig.BalancerConfig{Name: roundrobin.Name}, 552 }, 553 wantAddrs: []resolver.Address{ 554 testAddrWithAttrs("addr-1-1", 20, 90, "test-priority", &internal.LocalityID{Zone: "test-zone-1"}), 555 testAddrWithAttrs("addr-1-2", 20, 10, "test-priority", &internal.LocalityID{Zone: "test-zone-1"}), 556 testAddrWithAttrs("addr-2-1", 80, 90, "test-priority", &internal.LocalityID{Zone: "test-zone-2"}), 557 testAddrWithAttrs("addr-2-2", 80, 10, "test-priority", &internal.LocalityID{Zone: "test-zone-2"}), 558 }, 559 }, 560 { 561 name: "ring_hash as child", 562 localities: []xdsresource.Locality{ 563 { 564 Endpoints: []xdsresource.Endpoint{ 565 {Address: "addr-1-1", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 90}, 566 {Address: "addr-1-2", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 10}, 567 }, 568 ID: internal.LocalityID{Zone: "test-zone-1"}, 569 Weight: 20, 570 }, 571 { 572 Endpoints: []xdsresource.Endpoint{ 573 {Address: "addr-2-1", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 90}, 574 {Address: "addr-2-2", HealthStatus: xdsresource.EndpointHealthStatusHealthy, Weight: 10}, 575 }, 576 ID: internal.LocalityID{Zone: "test-zone-2"}, 577 Weight: 80, 578 }, 579 }, 580 priorityName: "test-priority", 581 childPolicy: &iserviceconfig.BalancerConfig{Name: ringhash.Name, Config: &ringhash.LBConfig{MinRingSize: 1, MaxRingSize: 2}}, 582 // lrsServer is nil, so LRS policy will not be used. 583 wantConfig: &clusterimpl.LBConfig{ 584 ChildPolicy: &iserviceconfig.BalancerConfig{ 585 Name: ringhash.Name, 586 Config: &ringhash.LBConfig{MinRingSize: 1, MaxRingSize: 2}, 587 }, 588 }, 589 wantAddrs: []resolver.Address{ 590 testAddrWithAttrs("addr-1-1", 20, 90, "test-priority", &internal.LocalityID{Zone: "test-zone-1"}), 591 testAddrWithAttrs("addr-1-2", 20, 10, "test-priority", &internal.LocalityID{Zone: "test-zone-1"}), 592 testAddrWithAttrs("addr-2-1", 80, 90, "test-priority", &internal.LocalityID{Zone: "test-zone-2"}), 593 testAddrWithAttrs("addr-2-2", 80, 10, "test-priority", &internal.LocalityID{Zone: "test-zone-2"}), 594 }, 595 }, 596 } 597 for _, tt := range tests { 598 t.Run(tt.name, func(t *testing.T) { 599 got, got1, err := priorityLocalitiesToClusterImpl(tt.localities, tt.priorityName, tt.mechanism, nil, tt.childPolicy) 600 if (err != nil) != tt.wantErr { 601 t.Fatalf("priorityLocalitiesToClusterImpl() error = %v, wantErr %v", err, tt.wantErr) 602 } 603 if diff := cmp.Diff(got, tt.wantConfig); diff != "" { 604 t.Errorf("localitiesToWeightedTarget() diff (-got +want) %v", diff) 605 } 606 if diff := cmp.Diff(got1, tt.wantAddrs, cmp.AllowUnexported(attributes.Attributes{})); diff != "" { 607 t.Errorf("localitiesToWeightedTarget() diff (-got +want) %v", diff) 608 } 609 }) 610 } 611 } 612 613 func assertString(f func() (string, error)) string { 614 s, err := f() 615 if err != nil { 616 panic(err.Error()) 617 } 618 return s 619 } 620 621 func testAddrWithAttrs(addrStr string, localityWeight, endpointWeight uint32, priority string, lID *internal.LocalityID) resolver.Address { 622 addr := resolver.Address{Addr: addrStr} 623 path := []string{priority} 624 if lID != nil { 625 path = append(path, assertString(lID.ToString)) 626 addr = internal.SetLocalityID(addr, *lID) 627 } 628 addr = hierarchy.Set(addr, path) 629 addr = wrrlocality.SetAddrInfo(addr, wrrlocality.AddrInfo{LocalityWeight: localityWeight}) 630 addr = weightedroundrobin.SetAddrInfo(addr, weightedroundrobin.AddrInfo{Weight: localityWeight * endpointWeight}) 631 return addr 632 } 633 634 func TestConvertClusterImplMapToOutlierDetection(t *testing.T) { 635 tests := []struct { 636 name string 637 ciCfgsMap map[string]*clusterimpl.LBConfig 638 odCfg outlierdetection.LBConfig 639 wantODCfgs map[string]*outlierdetection.LBConfig 640 }{ 641 { 642 name: "single-entry-noop", 643 ciCfgsMap: map[string]*clusterimpl.LBConfig{ 644 "child1": { 645 Cluster: "cluster1", 646 }, 647 }, 648 odCfg: outlierdetection.LBConfig{ 649 Interval: 1<<63 - 1, 650 }, 651 wantODCfgs: map[string]*outlierdetection.LBConfig{ 652 "child1": { 653 Interval: 1<<63 - 1, 654 ChildPolicy: &iserviceconfig.BalancerConfig{ 655 Name: clusterimpl.Name, 656 Config: &clusterimpl.LBConfig{ 657 Cluster: "cluster1", 658 }, 659 }, 660 }, 661 }, 662 }, 663 { 664 name: "multiple-entries-noop", 665 ciCfgsMap: map[string]*clusterimpl.LBConfig{ 666 "child1": { 667 Cluster: "cluster1", 668 }, 669 "child2": { 670 Cluster: "cluster2", 671 }, 672 }, 673 odCfg: outlierdetection.LBConfig{ 674 Interval: 1<<63 - 1, 675 }, 676 wantODCfgs: map[string]*outlierdetection.LBConfig{ 677 "child1": { 678 Interval: 1<<63 - 1, 679 ChildPolicy: &iserviceconfig.BalancerConfig{ 680 Name: clusterimpl.Name, 681 Config: &clusterimpl.LBConfig{ 682 Cluster: "cluster1", 683 }, 684 }, 685 }, 686 "child2": { 687 Interval: 1<<63 - 1, 688 ChildPolicy: &iserviceconfig.BalancerConfig{ 689 Name: clusterimpl.Name, 690 Config: &clusterimpl.LBConfig{ 691 Cluster: "cluster2", 692 }, 693 }, 694 }, 695 }, 696 }, 697 } 698 for _, test := range tests { 699 t.Run(test.name, func(t *testing.T) { 700 got := convertClusterImplMapToOutlierDetection(test.ciCfgsMap, test.odCfg) 701 if diff := cmp.Diff(got, test.wantODCfgs); diff != "" { 702 t.Fatalf("convertClusterImplMapToOutlierDetection() diff(-got +want) %v", diff) 703 } 704 }) 705 } 706 }