google.golang.org/grpc@v1.62.1/xds/internal/balancer/clusterimpl/balancer_test.go (about) 1 /* 2 * 3 * Copyright 2020 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 clusterimpl 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/google/go-cmp/cmp" 30 "github.com/google/go-cmp/cmp/cmpopts" 31 "google.golang.org/grpc/balancer" 32 "google.golang.org/grpc/balancer/base" 33 "google.golang.org/grpc/balancer/roundrobin" 34 "google.golang.org/grpc/connectivity" 35 "google.golang.org/grpc/internal" 36 "google.golang.org/grpc/internal/balancer/stub" 37 "google.golang.org/grpc/internal/grpctest" 38 internalserviceconfig "google.golang.org/grpc/internal/serviceconfig" 39 "google.golang.org/grpc/internal/testutils" 40 "google.golang.org/grpc/resolver" 41 xdsinternal "google.golang.org/grpc/xds/internal" 42 "google.golang.org/grpc/xds/internal/testutils/fakeclient" 43 "google.golang.org/grpc/xds/internal/xdsclient" 44 "google.golang.org/grpc/xds/internal/xdsclient/bootstrap" 45 "google.golang.org/grpc/xds/internal/xdsclient/load" 46 ) 47 48 const ( 49 defaultTestTimeout = 5 * time.Second 50 defaultShortTestTimeout = 100 * time.Microsecond 51 52 testClusterName = "test-cluster" 53 testServiceName = "test-eds-service" 54 ) 55 56 var ( 57 testBackendAddrs = []resolver.Address{ 58 {Addr: "1.1.1.1:1"}, 59 } 60 testLRSServerConfig = &bootstrap.ServerConfig{ 61 ServerURI: "trafficdirector.googleapis.com:443", 62 Creds: bootstrap.ChannelCreds{ 63 Type: "google_default", 64 }, 65 } 66 67 cmpOpts = cmp.Options{ 68 cmpopts.EquateEmpty(), 69 cmpopts.IgnoreFields(load.Data{}, "ReportInterval"), 70 } 71 ) 72 73 type s struct { 74 grpctest.Tester 75 } 76 77 func Test(t *testing.T) { 78 grpctest.RunSubTests(t, s{}) 79 } 80 81 func init() { 82 NewRandomWRR = testutils.NewTestWRR 83 } 84 85 // TestDropByCategory verifies that the balancer correctly drops the picks, and 86 // that the drops are reported. 87 func (s) TestDropByCategory(t *testing.T) { 88 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 89 defer cancel() 90 91 defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName) 92 xdsC := fakeclient.NewClient() 93 94 builder := balancer.Get(Name) 95 cc := testutils.NewBalancerClientConn(t) 96 b := builder.Build(cc, balancer.BuildOptions{}) 97 defer b.Close() 98 99 const ( 100 dropReason = "test-dropping-category" 101 dropNumerator = 1 102 dropDenominator = 2 103 ) 104 if err := b.UpdateClientConnState(balancer.ClientConnState{ 105 ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC), 106 BalancerConfig: &LBConfig{ 107 Cluster: testClusterName, 108 EDSServiceName: testServiceName, 109 LoadReportingServer: testLRSServerConfig, 110 DropCategories: []DropConfig{{ 111 Category: dropReason, 112 RequestsPerMillion: million * dropNumerator / dropDenominator, 113 }}, 114 ChildPolicy: &internalserviceconfig.BalancerConfig{ 115 Name: roundrobin.Name, 116 }, 117 }, 118 }); err != nil { 119 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 120 } 121 122 got, err := xdsC.WaitForReportLoad(ctx) 123 if err != nil { 124 t.Fatalf("xdsClient.ReportLoad failed with error: %v", err) 125 } 126 if got.Server != testLRSServerConfig { 127 t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig) 128 } 129 130 sc1 := <-cc.NewSubConnCh 131 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 132 // This should get the connecting picker. 133 if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil { 134 t.Fatal(err.Error()) 135 } 136 137 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 138 // Test pick with one backend. 139 140 const rpcCount = 20 141 if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error { 142 for i := 0; i < rpcCount; i++ { 143 gotSCSt, err := p.Pick(balancer.PickInfo{}) 144 // Even RPCs are dropped. 145 if i%2 == 0 { 146 if err == nil || !strings.Contains(err.Error(), "dropped") { 147 return fmt.Errorf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err) 148 } 149 continue 150 } 151 if err != nil || gotSCSt.SubConn != sc1 { 152 return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1) 153 } 154 if gotSCSt.Done != nil { 155 gotSCSt.Done(balancer.DoneInfo{}) 156 } 157 } 158 return nil 159 }); err != nil { 160 t.Fatal(err.Error()) 161 } 162 163 // Dump load data from the store and compare with expected counts. 164 loadStore := xdsC.LoadStore() 165 if loadStore == nil { 166 t.Fatal("loadStore is nil in xdsClient") 167 } 168 const dropCount = rpcCount * dropNumerator / dropDenominator 169 wantStatsData0 := []*load.Data{{ 170 Cluster: testClusterName, 171 Service: testServiceName, 172 TotalDrops: dropCount, 173 Drops: map[string]uint64{dropReason: dropCount}, 174 LocalityStats: map[string]load.LocalityData{ 175 assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: rpcCount - dropCount}}, 176 }, 177 }} 178 179 gotStatsData0 := loadStore.Stats([]string{testClusterName}) 180 if diff := cmp.Diff(gotStatsData0, wantStatsData0, cmpOpts); diff != "" { 181 t.Fatalf("got unexpected reports, diff (-got, +want): %v", diff) 182 } 183 184 // Send an update with new drop configs. 185 const ( 186 dropReason2 = "test-dropping-category-2" 187 dropNumerator2 = 1 188 dropDenominator2 = 4 189 ) 190 if err := b.UpdateClientConnState(balancer.ClientConnState{ 191 ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC), 192 BalancerConfig: &LBConfig{ 193 Cluster: testClusterName, 194 EDSServiceName: testServiceName, 195 LoadReportingServer: testLRSServerConfig, 196 DropCategories: []DropConfig{{ 197 Category: dropReason2, 198 RequestsPerMillion: million * dropNumerator2 / dropDenominator2, 199 }}, 200 ChildPolicy: &internalserviceconfig.BalancerConfig{ 201 Name: roundrobin.Name, 202 }, 203 }, 204 }); err != nil { 205 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 206 } 207 208 if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error { 209 for i := 0; i < rpcCount; i++ { 210 gotSCSt, err := p.Pick(balancer.PickInfo{}) 211 // Even RPCs are dropped. 212 if i%4 == 0 { 213 if err == nil || !strings.Contains(err.Error(), "dropped") { 214 return fmt.Errorf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err) 215 } 216 continue 217 } 218 if err != nil || gotSCSt.SubConn != sc1 { 219 return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1) 220 } 221 if gotSCSt.Done != nil { 222 gotSCSt.Done(balancer.DoneInfo{}) 223 } 224 } 225 return nil 226 }); err != nil { 227 t.Fatal(err.Error()) 228 } 229 230 const dropCount2 = rpcCount * dropNumerator2 / dropDenominator2 231 wantStatsData1 := []*load.Data{{ 232 Cluster: testClusterName, 233 Service: testServiceName, 234 TotalDrops: dropCount2, 235 Drops: map[string]uint64{dropReason2: dropCount2}, 236 LocalityStats: map[string]load.LocalityData{ 237 assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: rpcCount - dropCount2}}, 238 }, 239 }} 240 241 gotStatsData1 := loadStore.Stats([]string{testClusterName}) 242 if diff := cmp.Diff(gotStatsData1, wantStatsData1, cmpOpts); diff != "" { 243 t.Fatalf("got unexpected reports, diff (-got, +want): %v", diff) 244 } 245 } 246 247 // TestDropCircuitBreaking verifies that the balancer correctly drops the picks 248 // due to circuit breaking, and that the drops are reported. 249 func (s) TestDropCircuitBreaking(t *testing.T) { 250 defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName) 251 xdsC := fakeclient.NewClient() 252 253 builder := balancer.Get(Name) 254 cc := testutils.NewBalancerClientConn(t) 255 b := builder.Build(cc, balancer.BuildOptions{}) 256 defer b.Close() 257 258 var maxRequest uint32 = 50 259 if err := b.UpdateClientConnState(balancer.ClientConnState{ 260 ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC), 261 BalancerConfig: &LBConfig{ 262 Cluster: testClusterName, 263 EDSServiceName: testServiceName, 264 LoadReportingServer: testLRSServerConfig, 265 MaxConcurrentRequests: &maxRequest, 266 ChildPolicy: &internalserviceconfig.BalancerConfig{ 267 Name: roundrobin.Name, 268 }, 269 }, 270 }); err != nil { 271 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 272 } 273 274 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 275 defer cancel() 276 277 got, err := xdsC.WaitForReportLoad(ctx) 278 if err != nil { 279 t.Fatalf("xdsClient.ReportLoad failed with error: %v", err) 280 } 281 if got.Server != testLRSServerConfig { 282 t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig) 283 } 284 285 sc1 := <-cc.NewSubConnCh 286 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 287 // This should get the connecting picker. 288 if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil { 289 t.Fatal(err.Error()) 290 } 291 292 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 293 // Test pick with one backend. 294 const rpcCount = 100 295 if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error { 296 dones := []func(){} 297 for i := 0; i < rpcCount; i++ { 298 gotSCSt, err := p.Pick(balancer.PickInfo{}) 299 if i < 50 && err != nil { 300 return fmt.Errorf("The first 50%% picks should be non-drops, got error %v", err) 301 } else if i > 50 && err == nil { 302 return fmt.Errorf("The second 50%% picks should be drops, got error <nil>") 303 } 304 dones = append(dones, func() { 305 if gotSCSt.Done != nil { 306 gotSCSt.Done(balancer.DoneInfo{}) 307 } 308 }) 309 } 310 for _, done := range dones { 311 done() 312 } 313 314 dones = []func(){} 315 // Pick without drops. 316 for i := 0; i < 50; i++ { 317 gotSCSt, err := p.Pick(balancer.PickInfo{}) 318 if err != nil { 319 t.Errorf("The third 50%% picks should be non-drops, got error %v", err) 320 } 321 dones = append(dones, func() { 322 if gotSCSt.Done != nil { 323 gotSCSt.Done(balancer.DoneInfo{}) 324 } 325 }) 326 } 327 for _, done := range dones { 328 done() 329 } 330 331 return nil 332 }); err != nil { 333 t.Fatal(err.Error()) 334 } 335 336 // Dump load data from the store and compare with expected counts. 337 loadStore := xdsC.LoadStore() 338 if loadStore == nil { 339 t.Fatal("loadStore is nil in xdsClient") 340 } 341 342 wantStatsData0 := []*load.Data{{ 343 Cluster: testClusterName, 344 Service: testServiceName, 345 TotalDrops: uint64(maxRequest), 346 LocalityStats: map[string]load.LocalityData{ 347 assertString(xdsinternal.LocalityID{}.ToString): {RequestStats: load.RequestData{Succeeded: uint64(rpcCount - maxRequest + 50)}}, 348 }, 349 }} 350 351 gotStatsData0 := loadStore.Stats([]string{testClusterName}) 352 if diff := cmp.Diff(gotStatsData0, wantStatsData0, cmpOpts); diff != "" { 353 t.Fatalf("got unexpected drop reports, diff (-got, +want): %v", diff) 354 } 355 } 356 357 // TestPickerUpdateAfterClose covers the case where a child policy sends a 358 // picker update after the cluster_impl policy is closed. Because picker updates 359 // are handled in the run() goroutine, which exits before Close() returns, we 360 // expect the above picker update to be dropped. 361 func (s) TestPickerUpdateAfterClose(t *testing.T) { 362 defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName) 363 xdsC := fakeclient.NewClient() 364 365 builder := balancer.Get(Name) 366 cc := testutils.NewBalancerClientConn(t) 367 b := builder.Build(cc, balancer.BuildOptions{}) 368 369 // Create a stub balancer which waits for the cluster_impl policy to be 370 // closed before sending a picker update (upon receipt of a subConn state 371 // change). 372 closeCh := make(chan struct{}) 373 const childPolicyName = "stubBalancer-TestPickerUpdateAfterClose" 374 stub.Register(childPolicyName, stub.BalancerFuncs{ 375 UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error { 376 // Create a subConn which will be used later on to test the race 377 // between StateListener() and Close(). 378 sc, err := bd.ClientConn.NewSubConn(ccs.ResolverState.Addresses, balancer.NewSubConnOptions{ 379 StateListener: func(balancer.SubConnState) { 380 go func() { 381 // Wait for Close() to be called on the parent policy before 382 // sending the picker update. 383 <-closeCh 384 bd.ClientConn.UpdateState(balancer.State{ 385 Picker: base.NewErrPicker(errors.New("dummy error picker")), 386 }) 387 }() 388 }, 389 }) 390 if err != nil { 391 return err 392 } 393 sc.Connect() 394 return nil 395 }, 396 }) 397 398 var maxRequest uint32 = 50 399 if err := b.UpdateClientConnState(balancer.ClientConnState{ 400 ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC), 401 BalancerConfig: &LBConfig{ 402 Cluster: testClusterName, 403 EDSServiceName: testServiceName, 404 MaxConcurrentRequests: &maxRequest, 405 ChildPolicy: &internalserviceconfig.BalancerConfig{ 406 Name: childPolicyName, 407 }, 408 }, 409 }); err != nil { 410 b.Close() 411 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 412 } 413 414 // Send a subConn state change to trigger a picker update. The stub balancer 415 // that we use as the child policy will not send a picker update until the 416 // parent policy is closed. 417 sc1 := <-cc.NewSubConnCh 418 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 419 b.Close() 420 close(closeCh) 421 422 select { 423 case <-cc.NewPickerCh: 424 t.Fatalf("unexpected picker update after balancer is closed") 425 case <-time.After(defaultShortTestTimeout): 426 } 427 } 428 429 // TestClusterNameInAddressAttributes covers the case that cluster name is 430 // attached to the subconn address attributes. 431 func (s) TestClusterNameInAddressAttributes(t *testing.T) { 432 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 433 defer cancel() 434 435 defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName) 436 xdsC := fakeclient.NewClient() 437 438 builder := balancer.Get(Name) 439 cc := testutils.NewBalancerClientConn(t) 440 b := builder.Build(cc, balancer.BuildOptions{}) 441 defer b.Close() 442 443 if err := b.UpdateClientConnState(balancer.ClientConnState{ 444 ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC), 445 BalancerConfig: &LBConfig{ 446 Cluster: testClusterName, 447 EDSServiceName: testServiceName, 448 ChildPolicy: &internalserviceconfig.BalancerConfig{ 449 Name: roundrobin.Name, 450 }, 451 }, 452 }); err != nil { 453 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 454 } 455 456 sc1 := <-cc.NewSubConnCh 457 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 458 // This should get the connecting picker. 459 if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil { 460 t.Fatal(err.Error()) 461 } 462 463 addrs1 := <-cc.NewSubConnAddrsCh 464 if got, want := addrs1[0].Addr, testBackendAddrs[0].Addr; got != want { 465 t.Fatalf("sc is created with addr %v, want %v", got, want) 466 } 467 cn, ok := internal.GetXDSHandshakeClusterName(addrs1[0].Attributes) 468 if !ok || cn != testClusterName { 469 t.Fatalf("sc is created with addr with cluster name %v, %v, want cluster name %v", cn, ok, testClusterName) 470 } 471 472 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 473 // Test pick with one backend. 474 if err := cc.WaitForRoundRobinPicker(ctx, sc1); err != nil { 475 t.Fatal(err.Error()) 476 } 477 478 const testClusterName2 = "test-cluster-2" 479 var addr2 = resolver.Address{Addr: "2.2.2.2"} 480 if err := b.UpdateClientConnState(balancer.ClientConnState{ 481 ResolverState: xdsclient.SetClient(resolver.State{Addresses: []resolver.Address{addr2}}, xdsC), 482 BalancerConfig: &LBConfig{ 483 Cluster: testClusterName2, 484 EDSServiceName: testServiceName, 485 ChildPolicy: &internalserviceconfig.BalancerConfig{ 486 Name: roundrobin.Name, 487 }, 488 }, 489 }); err != nil { 490 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 491 } 492 493 addrs2 := <-cc.NewSubConnAddrsCh 494 if got, want := addrs2[0].Addr, addr2.Addr; got != want { 495 t.Fatalf("sc is created with addr %v, want %v", got, want) 496 } 497 // New addresses should have the new cluster name. 498 cn2, ok := internal.GetXDSHandshakeClusterName(addrs2[0].Attributes) 499 if !ok || cn2 != testClusterName2 { 500 t.Fatalf("sc is created with addr with cluster name %v, %v, want cluster name %v", cn2, ok, testClusterName2) 501 } 502 } 503 504 // TestReResolution verifies that when a SubConn turns transient failure, 505 // re-resolution is triggered. 506 func (s) TestReResolution(t *testing.T) { 507 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 508 defer cancel() 509 510 defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName) 511 xdsC := fakeclient.NewClient() 512 513 builder := balancer.Get(Name) 514 cc := testutils.NewBalancerClientConn(t) 515 b := builder.Build(cc, balancer.BuildOptions{}) 516 defer b.Close() 517 518 if err := b.UpdateClientConnState(balancer.ClientConnState{ 519 ResolverState: xdsclient.SetClient(resolver.State{Addresses: testBackendAddrs}, xdsC), 520 BalancerConfig: &LBConfig{ 521 Cluster: testClusterName, 522 EDSServiceName: testServiceName, 523 ChildPolicy: &internalserviceconfig.BalancerConfig{ 524 Name: roundrobin.Name, 525 }, 526 }, 527 }); err != nil { 528 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 529 } 530 531 sc1 := <-cc.NewSubConnCh 532 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 533 // This should get the connecting picker. 534 if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil { 535 t.Fatal(err.Error()) 536 } 537 538 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 539 // This should get the transient failure picker. 540 if err := cc.WaitForErrPicker(ctx); err != nil { 541 t.Fatal(err.Error()) 542 } 543 544 // The transient failure should trigger a re-resolution. 545 select { 546 case <-cc.ResolveNowCh: 547 case <-time.After(defaultTestTimeout): 548 t.Fatalf("timeout waiting for ResolveNow()") 549 } 550 551 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 552 // Test pick with one backend. 553 if err := cc.WaitForRoundRobinPicker(ctx, sc1); err != nil { 554 t.Fatal(err.Error()) 555 } 556 557 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 558 // This should get the transient failure picker. 559 if err := cc.WaitForErrPicker(ctx); err != nil { 560 t.Fatal(err.Error()) 561 } 562 563 // The transient failure should trigger a re-resolution. 564 select { 565 case <-cc.ResolveNowCh: 566 case <-time.After(defaultTestTimeout): 567 t.Fatalf("timeout waiting for ResolveNow()") 568 } 569 } 570 571 func (s) TestLoadReporting(t *testing.T) { 572 var testLocality = xdsinternal.LocalityID{ 573 Region: "test-region", 574 Zone: "test-zone", 575 SubZone: "test-sub-zone", 576 } 577 578 xdsC := fakeclient.NewClient() 579 580 builder := balancer.Get(Name) 581 cc := testutils.NewBalancerClientConn(t) 582 b := builder.Build(cc, balancer.BuildOptions{}) 583 defer b.Close() 584 585 addrs := make([]resolver.Address, len(testBackendAddrs)) 586 for i, a := range testBackendAddrs { 587 addrs[i] = xdsinternal.SetLocalityID(a, testLocality) 588 } 589 if err := b.UpdateClientConnState(balancer.ClientConnState{ 590 ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC), 591 BalancerConfig: &LBConfig{ 592 Cluster: testClusterName, 593 EDSServiceName: testServiceName, 594 LoadReportingServer: testLRSServerConfig, 595 // Locality: testLocality, 596 ChildPolicy: &internalserviceconfig.BalancerConfig{ 597 Name: roundrobin.Name, 598 }, 599 }, 600 }); err != nil { 601 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 602 } 603 604 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 605 defer cancel() 606 607 got, err := xdsC.WaitForReportLoad(ctx) 608 if err != nil { 609 t.Fatalf("xdsClient.ReportLoad failed with error: %v", err) 610 } 611 if got.Server != testLRSServerConfig { 612 t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig) 613 } 614 615 sc1 := <-cc.NewSubConnCh 616 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 617 // This should get the connecting picker. 618 if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil { 619 t.Fatal(err.Error()) 620 } 621 622 sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready}) 623 // Test pick with one backend. 624 const successCount = 5 625 const errorCount = 5 626 if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error { 627 for i := 0; i < successCount; i++ { 628 gotSCSt, err := p.Pick(balancer.PickInfo{}) 629 if gotSCSt.SubConn != sc1 { 630 return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1) 631 } 632 gotSCSt.Done(balancer.DoneInfo{}) 633 } 634 for i := 0; i < errorCount; i++ { 635 gotSCSt, err := p.Pick(balancer.PickInfo{}) 636 if gotSCSt.SubConn != sc1 { 637 return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1) 638 } 639 gotSCSt.Done(balancer.DoneInfo{Err: fmt.Errorf("error")}) 640 } 641 return nil 642 }); err != nil { 643 t.Fatal(err.Error()) 644 } 645 646 // Dump load data from the store and compare with expected counts. 647 loadStore := xdsC.LoadStore() 648 if loadStore == nil { 649 t.Fatal("loadStore is nil in xdsClient") 650 } 651 sds := loadStore.Stats([]string{testClusterName}) 652 if len(sds) == 0 { 653 t.Fatalf("loads for cluster %v not found in store", testClusterName) 654 } 655 sd := sds[0] 656 if sd.Cluster != testClusterName || sd.Service != testServiceName { 657 t.Fatalf("got unexpected load for %q, %q, want %q, %q", sd.Cluster, sd.Service, testClusterName, testServiceName) 658 } 659 testLocalityJSON, _ := testLocality.ToString() 660 localityData, ok := sd.LocalityStats[testLocalityJSON] 661 if !ok { 662 t.Fatalf("loads for %v not found in store", testLocality) 663 } 664 reqStats := localityData.RequestStats 665 if reqStats.Succeeded != successCount { 666 t.Errorf("got succeeded %v, want %v", reqStats.Succeeded, successCount) 667 } 668 if reqStats.Errored != errorCount { 669 t.Errorf("got errord %v, want %v", reqStats.Errored, errorCount) 670 } 671 if reqStats.InProgress != 0 { 672 t.Errorf("got inProgress %v, want %v", reqStats.InProgress, 0) 673 } 674 675 b.Close() 676 if err := xdsC.WaitForCancelReportLoad(ctx); err != nil { 677 t.Fatalf("unexpected error waiting form load report to be canceled: %v", err) 678 } 679 } 680 681 // TestUpdateLRSServer covers the cases 682 // - the init config specifies "" as the LRS server 683 // - config modifies LRS server to a different string 684 // - config sets LRS server to nil to stop load reporting 685 func (s) TestUpdateLRSServer(t *testing.T) { 686 var testLocality = xdsinternal.LocalityID{ 687 Region: "test-region", 688 Zone: "test-zone", 689 SubZone: "test-sub-zone", 690 } 691 692 xdsC := fakeclient.NewClient() 693 694 builder := balancer.Get(Name) 695 cc := testutils.NewBalancerClientConn(t) 696 b := builder.Build(cc, balancer.BuildOptions{}) 697 defer b.Close() 698 699 addrs := make([]resolver.Address, len(testBackendAddrs)) 700 for i, a := range testBackendAddrs { 701 addrs[i] = xdsinternal.SetLocalityID(a, testLocality) 702 } 703 if err := b.UpdateClientConnState(balancer.ClientConnState{ 704 ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC), 705 BalancerConfig: &LBConfig{ 706 Cluster: testClusterName, 707 EDSServiceName: testServiceName, 708 LoadReportingServer: testLRSServerConfig, 709 ChildPolicy: &internalserviceconfig.BalancerConfig{ 710 Name: roundrobin.Name, 711 }, 712 }, 713 }); err != nil { 714 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 715 } 716 717 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 718 defer cancel() 719 720 got, err := xdsC.WaitForReportLoad(ctx) 721 if err != nil { 722 t.Fatalf("xdsClient.ReportLoad failed with error: %v", err) 723 } 724 if got.Server != testLRSServerConfig { 725 t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig) 726 } 727 728 testLRSServerConfig2 := &bootstrap.ServerConfig{ 729 ServerURI: "trafficdirector-another.googleapis.com:443", 730 Creds: bootstrap.ChannelCreds{ 731 Type: "google_default", 732 }, 733 } 734 // Update LRS server to a different name. 735 if err := b.UpdateClientConnState(balancer.ClientConnState{ 736 ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC), 737 BalancerConfig: &LBConfig{ 738 Cluster: testClusterName, 739 EDSServiceName: testServiceName, 740 LoadReportingServer: testLRSServerConfig2, 741 ChildPolicy: &internalserviceconfig.BalancerConfig{ 742 Name: roundrobin.Name, 743 }, 744 }, 745 }); err != nil { 746 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 747 } 748 if err := xdsC.WaitForCancelReportLoad(ctx); err != nil { 749 t.Fatalf("unexpected error waiting form load report to be canceled: %v", err) 750 } 751 got2, err2 := xdsC.WaitForReportLoad(ctx) 752 if err2 != nil { 753 t.Fatalf("xdsClient.ReportLoad failed with error: %v", err2) 754 } 755 if got2.Server != testLRSServerConfig2 { 756 t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got2.Server, testLRSServerConfig2) 757 } 758 759 // Update LRS server to nil, to disable LRS. 760 if err := b.UpdateClientConnState(balancer.ClientConnState{ 761 ResolverState: xdsclient.SetClient(resolver.State{Addresses: addrs}, xdsC), 762 BalancerConfig: &LBConfig{ 763 Cluster: testClusterName, 764 EDSServiceName: testServiceName, 765 ChildPolicy: &internalserviceconfig.BalancerConfig{ 766 Name: roundrobin.Name, 767 }, 768 }, 769 }); err != nil { 770 t.Fatalf("unexpected error from UpdateClientConnState: %v", err) 771 } 772 if err := xdsC.WaitForCancelReportLoad(ctx); err != nil { 773 t.Fatalf("unexpected error waiting form load report to be canceled: %v", err) 774 } 775 776 shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultShortTestTimeout) 777 defer shortCancel() 778 if s, err := xdsC.WaitForReportLoad(shortCtx); err != context.DeadlineExceeded { 779 t.Fatalf("unexpected load report to server: %q", s) 780 } 781 } 782 783 func assertString(f func() (string, error)) string { 784 s, err := f() 785 if err != nil { 786 panic(err.Error()) 787 } 788 return s 789 }