github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/cdsbalancer/cdsbalancer_test.go (about) 1 /* 2 * Copyright 2019 gRPC authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cdsbalancer 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/google/go-cmp/cmp/cmpopts" 29 "github.com/hxx258456/ccgo/grpc/balancer" 30 "github.com/hxx258456/ccgo/grpc/connectivity" 31 "github.com/hxx258456/ccgo/grpc/internal" 32 "github.com/hxx258456/ccgo/grpc/internal/grpctest" 33 internalserviceconfig "github.com/hxx258456/ccgo/grpc/internal/serviceconfig" 34 "github.com/hxx258456/ccgo/grpc/internal/testutils" 35 "github.com/hxx258456/ccgo/grpc/resolver" 36 "github.com/hxx258456/ccgo/grpc/serviceconfig" 37 "github.com/hxx258456/ccgo/grpc/xds/internal/balancer/clusterresolver" 38 "github.com/hxx258456/ccgo/grpc/xds/internal/balancer/ringhash" 39 "github.com/hxx258456/ccgo/grpc/xds/internal/testutils/fakeclient" 40 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient" 41 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource" 42 ) 43 44 const ( 45 clusterName = "cluster1" 46 serviceName = "service1" 47 defaultTestTimeout = 5 * time.Second 48 defaultTestShortTimeout = 10 * time.Millisecond // For events expected to *not* happen. 49 ) 50 51 type s struct { 52 grpctest.Tester 53 } 54 55 func Test(t *testing.T) { 56 grpctest.RunSubTests(t, s{}) 57 } 58 59 // cdsWatchInfo wraps the update and the error sent in a CDS watch callback. 60 type cdsWatchInfo struct { 61 update xdsresource.ClusterUpdate 62 err error 63 } 64 65 // invokeWatchCb invokes the CDS watch callback registered by the cdsBalancer 66 // and waits for appropriate state to be pushed to the provided edsBalancer. 67 func invokeWatchCbAndWait(ctx context.Context, xdsC *fakeclient.Client, cdsW cdsWatchInfo, wantCCS balancer.ClientConnState, edsB *testEDSBalancer) error { 68 xdsC.InvokeWatchClusterCallback(cdsW.update, cdsW.err) 69 if cdsW.err != nil { 70 return edsB.waitForResolverError(ctx, cdsW.err) 71 } 72 return edsB.waitForClientConnUpdate(ctx, wantCCS) 73 } 74 75 // testEDSBalancer is a fake edsBalancer used to verify different actions from 76 // the cdsBalancer. It contains a bunch of channels to signal different events 77 // to the test. 78 type testEDSBalancer struct { 79 // ccsCh is a channel used to signal the receipt of a ClientConn update. 80 ccsCh *testutils.Channel 81 // scStateCh is a channel used to signal the receipt of a SubConn update. 82 scStateCh *testutils.Channel 83 // resolverErrCh is a channel used to signal a resolver error. 84 resolverErrCh *testutils.Channel 85 // closeCh is a channel used to signal the closing of this balancer. 86 closeCh *testutils.Channel 87 exitIdleCh *testutils.Channel 88 // parentCC is the balancer.ClientConn passed to this test balancer as part 89 // of the Build() call. 90 parentCC balancer.ClientConn 91 } 92 93 type subConnWithState struct { 94 sc balancer.SubConn 95 state balancer.SubConnState 96 } 97 98 func newTestEDSBalancer() *testEDSBalancer { 99 return &testEDSBalancer{ 100 ccsCh: testutils.NewChannel(), 101 scStateCh: testutils.NewChannel(), 102 resolverErrCh: testutils.NewChannel(), 103 closeCh: testutils.NewChannel(), 104 exitIdleCh: testutils.NewChannel(), 105 } 106 } 107 108 func (tb *testEDSBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error { 109 tb.ccsCh.Send(ccs) 110 return nil 111 } 112 113 func (tb *testEDSBalancer) ResolverError(err error) { 114 tb.resolverErrCh.Send(err) 115 } 116 117 func (tb *testEDSBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { 118 tb.scStateCh.Send(subConnWithState{sc: sc, state: state}) 119 } 120 121 func (tb *testEDSBalancer) Close() { 122 tb.closeCh.Send(struct{}{}) 123 } 124 125 func (tb *testEDSBalancer) ExitIdle() { 126 tb.exitIdleCh.Send(struct{}{}) 127 } 128 129 // waitForClientConnUpdate verifies if the testEDSBalancer receives the 130 // provided ClientConnState within a reasonable amount of time. 131 func (tb *testEDSBalancer) waitForClientConnUpdate(ctx context.Context, wantCCS balancer.ClientConnState) error { 132 ccs, err := tb.ccsCh.Receive(ctx) 133 if err != nil { 134 return err 135 } 136 gotCCS := ccs.(balancer.ClientConnState) 137 if xdsclient.FromResolverState(gotCCS.ResolverState) == nil { 138 return fmt.Errorf("want resolver state with XDSClient attached, got one without") 139 } 140 if diff := cmp.Diff(gotCCS, wantCCS, cmpopts.IgnoreFields(resolver.State{}, "Attributes")); diff != "" { 141 return fmt.Errorf("received unexpected ClientConnState, diff (-got +want): %v", diff) 142 } 143 return nil 144 } 145 146 // waitForSubConnUpdate verifies if the testEDSBalancer receives the provided 147 // SubConn update before the context expires. 148 func (tb *testEDSBalancer) waitForSubConnUpdate(ctx context.Context, wantSCS subConnWithState) error { 149 scs, err := tb.scStateCh.Receive(ctx) 150 if err != nil { 151 return err 152 } 153 gotSCS := scs.(subConnWithState) 154 if !cmp.Equal(gotSCS, wantSCS, cmp.AllowUnexported(subConnWithState{})) { 155 return fmt.Errorf("received SubConnState: %+v, want %+v", gotSCS, wantSCS) 156 } 157 return nil 158 } 159 160 // waitForResolverError verifies if the testEDSBalancer receives the provided 161 // resolver error before the context expires. 162 func (tb *testEDSBalancer) waitForResolverError(ctx context.Context, wantErr error) error { 163 gotErr, err := tb.resolverErrCh.Receive(ctx) 164 if err != nil { 165 return err 166 } 167 if gotErr != wantErr { 168 return fmt.Errorf("received resolver error: %v, want %v", gotErr, wantErr) 169 } 170 return nil 171 } 172 173 // waitForClose verifies that the edsBalancer is closed before the context 174 // expires. 175 func (tb *testEDSBalancer) waitForClose(ctx context.Context) error { 176 if _, err := tb.closeCh.Receive(ctx); err != nil { 177 return err 178 } 179 return nil 180 } 181 182 // cdsCCS is a helper function to construct a good update passed from the 183 // xdsResolver to the cdsBalancer. 184 func cdsCCS(cluster string, xdsC xdsclient.XDSClient) balancer.ClientConnState { 185 const cdsLBConfig = `{ 186 "loadBalancingConfig":[ 187 { 188 "cds_experimental":{ 189 "Cluster": "%s" 190 } 191 } 192 ] 193 }` 194 jsonSC := fmt.Sprintf(cdsLBConfig, cluster) 195 return balancer.ClientConnState{ 196 ResolverState: xdsclient.SetClient(resolver.State{ 197 ServiceConfig: internal.ParseServiceConfigForTesting.(func(string) *serviceconfig.ParseResult)(jsonSC), 198 }, xdsC), 199 BalancerConfig: &lbConfig{ClusterName: clusterName}, 200 } 201 } 202 203 // edsCCS is a helper function to construct a good update passed from the 204 // cdsBalancer to the edsBalancer. 205 func edsCCS(service string, countMax *uint32, enableLRS bool, xdslbpolicy *internalserviceconfig.BalancerConfig) balancer.ClientConnState { 206 discoveryMechanism := clusterresolver.DiscoveryMechanism{ 207 Type: clusterresolver.DiscoveryMechanismTypeEDS, 208 Cluster: service, 209 MaxConcurrentRequests: countMax, 210 } 211 if enableLRS { 212 discoveryMechanism.LoadReportingServerName = new(string) 213 214 } 215 lbCfg := &clusterresolver.LBConfig{ 216 DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{discoveryMechanism}, 217 XDSLBPolicy: xdslbpolicy, 218 } 219 220 return balancer.ClientConnState{ 221 BalancerConfig: lbCfg, 222 } 223 } 224 225 // setup creates a cdsBalancer and an edsBalancer (and overrides the 226 // newChildBalancer function to return it), and also returns a cleanup function. 227 func setup(t *testing.T) (*fakeclient.Client, *cdsBalancer, *testEDSBalancer, *testutils.TestClientConn, func()) { 228 t.Helper() 229 xdsC := fakeclient.NewClient() 230 builder := balancer.Get(cdsName) 231 if builder == nil { 232 t.Fatalf("balancer.Get(%q) returned nil", cdsName) 233 } 234 tcc := testutils.NewTestClientConn(t) 235 cdsB := builder.Build(tcc, balancer.BuildOptions{}) 236 237 edsB := newTestEDSBalancer() 238 oldEDSBalancerBuilder := newChildBalancer 239 newChildBalancer = func(cc balancer.ClientConn, opts balancer.BuildOptions) (balancer.Balancer, error) { 240 edsB.parentCC = cc 241 return edsB, nil 242 } 243 244 return xdsC, cdsB.(*cdsBalancer), edsB, tcc, func() { 245 newChildBalancer = oldEDSBalancerBuilder 246 xdsC.Close() 247 } 248 } 249 250 // setupWithWatch does everything that setup does, and also pushes a ClientConn 251 // update to the cdsBalancer and waits for a CDS watch call to be registered. 252 func setupWithWatch(t *testing.T) (*fakeclient.Client, *cdsBalancer, *testEDSBalancer, *testutils.TestClientConn, func()) { 253 t.Helper() 254 255 xdsC, cdsB, edsB, tcc, cancel := setup(t) 256 if err := cdsB.UpdateClientConnState(cdsCCS(clusterName, xdsC)); err != nil { 257 t.Fatalf("cdsBalancer.UpdateClientConnState failed with error: %v", err) 258 } 259 260 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 261 defer ctxCancel() 262 gotCluster, err := xdsC.WaitForWatchCluster(ctx) 263 if err != nil { 264 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 265 } 266 if gotCluster != clusterName { 267 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, clusterName) 268 } 269 return xdsC, cdsB, edsB, tcc, cancel 270 } 271 272 // TestUpdateClientConnState invokes the UpdateClientConnState method on the 273 // cdsBalancer with different inputs and verifies that the CDS watch API on the 274 // provided xdsClient is invoked appropriately. 275 func (s) TestUpdateClientConnState(t *testing.T) { 276 xdsC := fakeclient.NewClient() 277 defer xdsC.Close() 278 279 tests := []struct { 280 name string 281 ccs balancer.ClientConnState 282 wantErr error 283 wantCluster string 284 }{ 285 { 286 name: "bad-lbCfg-type", 287 ccs: balancer.ClientConnState{BalancerConfig: nil}, 288 wantErr: balancer.ErrBadResolverState, 289 }, 290 { 291 name: "empty-cluster-in-lbCfg", 292 ccs: balancer.ClientConnState{BalancerConfig: &lbConfig{ClusterName: ""}}, 293 wantErr: balancer.ErrBadResolverState, 294 }, 295 { 296 name: "happy-good-case", 297 ccs: cdsCCS(clusterName, xdsC), 298 wantCluster: clusterName, 299 }, 300 } 301 302 for _, test := range tests { 303 t.Run(test.name, func(t *testing.T) { 304 _, cdsB, _, _, cancel := setup(t) 305 defer func() { 306 cancel() 307 cdsB.Close() 308 }() 309 310 if err := cdsB.UpdateClientConnState(test.ccs); err != test.wantErr { 311 t.Fatalf("cdsBalancer.UpdateClientConnState failed with error: %v", err) 312 } 313 if test.wantErr != nil { 314 // When we wanted an error and got it, we should return early. 315 return 316 } 317 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 318 defer ctxCancel() 319 gotCluster, err := xdsC.WaitForWatchCluster(ctx) 320 if err != nil { 321 t.Fatalf("xdsClient.WatchCDS failed with error: %v", err) 322 } 323 if gotCluster != test.wantCluster { 324 t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, test.wantCluster) 325 } 326 }) 327 } 328 } 329 330 // TestUpdateClientConnStateWithSameState verifies that a ClientConnState 331 // update with the same cluster and xdsClient does not cause the cdsBalancer to 332 // create a new watch. 333 func (s) TestUpdateClientConnStateWithSameState(t *testing.T) { 334 xdsC, cdsB, _, _, cancel := setupWithWatch(t) 335 defer func() { 336 cancel() 337 cdsB.Close() 338 }() 339 340 // This is the same clientConn update sent in setupWithWatch(). 341 if err := cdsB.UpdateClientConnState(cdsCCS(clusterName, xdsC)); err != nil { 342 t.Fatalf("cdsBalancer.UpdateClientConnState failed with error: %v", err) 343 } 344 // The above update should not result in a new watch being registered. 345 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 346 defer ctxCancel() 347 if _, err := xdsC.WaitForWatchCluster(ctx); err != context.DeadlineExceeded { 348 t.Fatalf("waiting for WatchCluster() should have timed out, but returned error: %v", err) 349 } 350 } 351 352 // TestHandleClusterUpdate invokes the registered CDS watch callback with 353 // different updates and verifies that the expect ClientConnState is propagated 354 // to the edsBalancer. 355 func (s) TestHandleClusterUpdate(t *testing.T) { 356 xdsC, cdsB, edsB, _, cancel := setupWithWatch(t) 357 defer func() { 358 cancel() 359 cdsB.Close() 360 }() 361 362 tests := []struct { 363 name string 364 cdsUpdate xdsresource.ClusterUpdate 365 updateErr error 366 wantCCS balancer.ClientConnState 367 }{ 368 { 369 name: "happy-case-with-lrs", 370 cdsUpdate: xdsresource.ClusterUpdate{ClusterName: serviceName, EnableLRS: true}, 371 wantCCS: edsCCS(serviceName, nil, true, nil), 372 }, 373 { 374 name: "happy-case-without-lrs", 375 cdsUpdate: xdsresource.ClusterUpdate{ClusterName: serviceName}, 376 wantCCS: edsCCS(serviceName, nil, false, nil), 377 }, 378 { 379 name: "happy-case-with-ring-hash-lb-policy", 380 cdsUpdate: xdsresource.ClusterUpdate{ 381 ClusterName: serviceName, 382 LBPolicy: &xdsresource.ClusterLBPolicyRingHash{MinimumRingSize: 10, MaximumRingSize: 100}, 383 }, 384 wantCCS: edsCCS(serviceName, nil, false, &internalserviceconfig.BalancerConfig{ 385 Name: ringhash.Name, 386 Config: &ringhash.LBConfig{MinRingSize: 10, MaxRingSize: 100}, 387 }), 388 }, 389 } 390 391 for _, test := range tests { 392 t.Run(test.name, func(t *testing.T) { 393 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 394 defer ctxCancel() 395 if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{test.cdsUpdate, test.updateErr}, test.wantCCS, edsB); err != nil { 396 t.Fatal(err) 397 } 398 }) 399 } 400 } 401 402 // TestHandleClusterUpdateError covers the cases that an error is returned from 403 // the watcher. 404 func (s) TestHandleClusterUpdateError(t *testing.T) { 405 // This creates a CDS balancer, pushes a ClientConnState update with a fake 406 // xdsClient, and makes sure that the CDS balancer registers a watch on the 407 // provided xdsClient. 408 xdsC, cdsB, edsB, tcc, cancel := setupWithWatch(t) 409 defer func() { 410 cancel() 411 cdsB.Close() 412 }() 413 414 // A watch was registered above, but the watch callback has not been invoked 415 // yet. This means that the watch handler on the CDS balancer has not been 416 // invoked yet, and therefore no EDS balancer has been built so far. A 417 // resolver error at this point should result in the CDS balancer returning 418 // an error picker. 419 watcherErr := errors.New("cdsBalancer watcher error") 420 xdsC.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{}, watcherErr) 421 422 // Since the error being pushed here is not a resource-not-found-error, the 423 // registered watch should not be cancelled. 424 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 425 defer sCancel() 426 if _, err := xdsC.WaitForCancelClusterWatch(sCtx); err != context.DeadlineExceeded { 427 t.Fatal("cluster watch cancelled for a non-resource-not-found-error") 428 } 429 // The CDS balancer has not yet created an EDS balancer. So, this resolver 430 // error should not be forwarded to our fake EDS balancer. 431 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 432 defer sCancel() 433 if err := edsB.waitForResolverError(sCtx, watcherErr); err != context.DeadlineExceeded { 434 t.Fatal("eds balancer shouldn't get error (shouldn't be built yet)") 435 } 436 437 // Make sure the CDS balancer reports an error picker. 438 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 439 defer ctxCancel() 440 select { 441 case <-ctx.Done(): 442 t.Fatalf("timeout when waiting for an error picker") 443 case picker := <-tcc.NewPickerCh: 444 if _, perr := picker.Pick(balancer.PickInfo{}); perr == nil { 445 t.Fatalf("CDS balancer returned a picker which is not an error picker") 446 } 447 } 448 449 // Here we invoke the watch callback registered on the fake xdsClient. This 450 // will trigger the watch handler on the CDS balancer, which will attempt to 451 // create a new EDS balancer. The fake EDS balancer created above will be 452 // returned to the CDS balancer, because we have overridden the 453 // newChildBalancer function as part of test setup. 454 cdsUpdate := xdsresource.ClusterUpdate{ClusterName: serviceName} 455 wantCCS := edsCCS(serviceName, nil, false, nil) 456 if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil { 457 t.Fatal(err) 458 } 459 460 // Again push a non-resource-not-found-error through the watcher callback. 461 xdsC.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{}, watcherErr) 462 // Make sure the registered watch is not cancelled. 463 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 464 defer sCancel() 465 if _, err := xdsC.WaitForCancelClusterWatch(sCtx); err != context.DeadlineExceeded { 466 t.Fatal("cluster watch cancelled for a non-resource-not-found-error") 467 } 468 // Make sure the error is forwarded to the EDS balancer. 469 if err := edsB.waitForResolverError(ctx, watcherErr); err != nil { 470 t.Fatalf("Watch callback error is not forwarded to EDS balancer") 471 } 472 473 // Push a resource-not-found-error this time around. 474 resourceErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "cdsBalancer resource not found error") 475 xdsC.InvokeWatchClusterCallback(xdsresource.ClusterUpdate{}, resourceErr) 476 // Make sure that the watch is not cancelled. This error indicates that the 477 // request cluster resource is not found. We should continue to watch it. 478 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 479 defer sCancel() 480 if _, err := xdsC.WaitForCancelClusterWatch(sCtx); err != context.DeadlineExceeded { 481 t.Fatal("cluster watch cancelled for a resource-not-found-error") 482 } 483 // Make sure the error is forwarded to the EDS balancer. 484 if err := edsB.waitForResolverError(ctx, resourceErr); err != nil { 485 t.Fatalf("Watch callback error is not forwarded to EDS balancer") 486 } 487 } 488 489 // TestResolverError verifies the ResolverError() method in the CDS balancer. 490 func (s) TestResolverError(t *testing.T) { 491 // This creates a CDS balancer, pushes a ClientConnState update with a fake 492 // xdsClient, and makes sure that the CDS balancer registers a watch on the 493 // provided xdsClient. 494 xdsC, cdsB, edsB, tcc, cancel := setupWithWatch(t) 495 defer func() { 496 cancel() 497 cdsB.Close() 498 }() 499 500 // A watch was registered above, but the watch callback has not been invoked 501 // yet. This means that the watch handler on the CDS balancer has not been 502 // invoked yet, and therefore no EDS balancer has been built so far. A 503 // resolver error at this point should result in the CDS balancer returning 504 // an error picker. 505 resolverErr := errors.New("cdsBalancer resolver error") 506 cdsB.ResolverError(resolverErr) 507 508 // Since the error being pushed here is not a resource-not-found-error, the 509 // registered watch should not be cancelled. 510 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 511 defer sCancel() 512 if _, err := xdsC.WaitForCancelClusterWatch(sCtx); err != context.DeadlineExceeded { 513 t.Fatal("cluster watch cancelled for a non-resource-not-found-error") 514 } 515 // The CDS balancer has not yet created an EDS balancer. So, this resolver 516 // error should not be forwarded to our fake EDS balancer. 517 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 518 defer sCancel() 519 if err := edsB.waitForResolverError(sCtx, resolverErr); err != context.DeadlineExceeded { 520 t.Fatal("eds balancer shouldn't get error (shouldn't be built yet)") 521 } 522 // Make sure the CDS balancer reports an error picker. 523 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 524 defer ctxCancel() 525 select { 526 case <-ctx.Done(): 527 t.Fatalf("timeout when waiting for an error picker") 528 case picker := <-tcc.NewPickerCh: 529 if _, perr := picker.Pick(balancer.PickInfo{}); perr == nil { 530 t.Fatalf("CDS balancer returned a picker which is not an error picker") 531 } 532 } 533 534 // Here we invoke the watch callback registered on the fake xdsClient. This 535 // will trigger the watch handler on the CDS balancer, which will attempt to 536 // create a new EDS balancer. The fake EDS balancer created above will be 537 // returned to the CDS balancer, because we have overridden the 538 // newChildBalancer function as part of test setup. 539 cdsUpdate := xdsresource.ClusterUpdate{ClusterName: serviceName} 540 wantCCS := edsCCS(serviceName, nil, false, nil) 541 if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil { 542 t.Fatal(err) 543 } 544 545 // Again push a non-resource-not-found-error. 546 cdsB.ResolverError(resolverErr) 547 // Make sure the registered watch is not cancelled. 548 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 549 defer sCancel() 550 if _, err := xdsC.WaitForCancelClusterWatch(sCtx); err != context.DeadlineExceeded { 551 t.Fatal("cluster watch cancelled for a non-resource-not-found-error") 552 } 553 // Make sure the error is forwarded to the EDS balancer. 554 if err := edsB.waitForResolverError(ctx, resolverErr); err != nil { 555 t.Fatalf("ResolverError() not forwarded to EDS balancer") 556 } 557 558 // Push a resource-not-found-error this time around. 559 resourceErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "cdsBalancer resource not found error") 560 cdsB.ResolverError(resourceErr) 561 // Make sure the registered watch is cancelled. 562 if _, err := xdsC.WaitForCancelClusterWatch(ctx); err != nil { 563 t.Fatalf("want watch to be canceled, watchForCancel failed: %v", err) 564 } 565 // Make sure the error is forwarded to the EDS balancer. 566 if err := edsB.waitForResolverError(ctx, resourceErr); err != nil { 567 t.Fatalf("eds balancer should get resource-not-found error, waitForError failed: %v", err) 568 } 569 } 570 571 // TestUpdateSubConnState verifies the UpdateSubConnState() method in the CDS 572 // balancer. 573 func (s) TestUpdateSubConnState(t *testing.T) { 574 // This creates a CDS balancer, pushes a ClientConnState update with a fake 575 // xdsClient, and makes sure that the CDS balancer registers a watch on the 576 // provided xdsClient. 577 xdsC, cdsB, edsB, _, cancel := setupWithWatch(t) 578 defer func() { 579 cancel() 580 cdsB.Close() 581 }() 582 583 // Here we invoke the watch callback registered on the fake xdsClient. This 584 // will trigger the watch handler on the CDS balancer, which will attempt to 585 // create a new EDS balancer. The fake EDS balancer created above will be 586 // returned to the CDS balancer, because we have overridden the 587 // newChildBalancer function as part of test setup. 588 cdsUpdate := xdsresource.ClusterUpdate{ClusterName: serviceName} 589 wantCCS := edsCCS(serviceName, nil, false, nil) 590 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 591 defer ctxCancel() 592 if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil { 593 t.Fatal(err) 594 } 595 596 // Push a subConn state change to the CDS balancer. 597 var sc balancer.SubConn 598 state := balancer.SubConnState{ConnectivityState: connectivity.Ready} 599 cdsB.UpdateSubConnState(sc, state) 600 601 // Make sure that the update is forwarded to the EDS balancer. 602 if err := edsB.waitForSubConnUpdate(ctx, subConnWithState{sc: sc, state: state}); err != nil { 603 t.Fatal(err) 604 } 605 } 606 607 // TestCircuitBreaking verifies that the CDS balancer correctly updates a 608 // service's counter on watch updates. 609 func (s) TestCircuitBreaking(t *testing.T) { 610 // This creates a CDS balancer, pushes a ClientConnState update with a fake 611 // xdsClient, and makes sure that the CDS balancer registers a watch on the 612 // provided xdsClient. 613 xdsC, cdsB, edsB, _, cancel := setupWithXDSCreds(t) 614 defer func() { 615 cancel() 616 cdsB.Close() 617 }() 618 619 // Here we invoke the watch callback registered on the fake xdsClient. This 620 // will trigger the watch handler on the CDS balancer, which will update 621 // the service's counter with the new max requests. 622 var maxRequests uint32 = 1 623 cdsUpdate := xdsresource.ClusterUpdate{ClusterName: clusterName, MaxRequests: &maxRequests} 624 wantCCS := edsCCS(clusterName, &maxRequests, false, nil) 625 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 626 defer ctxCancel() 627 if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil { 628 t.Fatal(err) 629 } 630 631 // Since the counter's max requests was set to 1, the first request should 632 // succeed and the second should fail. 633 counter := xdsclient.GetClusterRequestsCounter(clusterName, "") 634 if err := counter.StartRequest(maxRequests); err != nil { 635 t.Fatal(err) 636 } 637 if err := counter.StartRequest(maxRequests); err == nil { 638 t.Fatal("unexpected success on start request over max") 639 } 640 counter.EndRequest() 641 } 642 643 // TestClose verifies the Close() method in the the CDS balancer. 644 func (s) TestClose(t *testing.T) { 645 // This creates a CDS balancer, pushes a ClientConnState update with a fake 646 // xdsClient, and makes sure that the CDS balancer registers a watch on the 647 // provided xdsClient. 648 xdsC, cdsB, edsB, _, cancel := setupWithWatch(t) 649 defer cancel() 650 651 // Here we invoke the watch callback registered on the fake xdsClient. This 652 // will trigger the watch handler on the CDS balancer, which will attempt to 653 // create a new EDS balancer. The fake EDS balancer created above will be 654 // returned to the CDS balancer, because we have overridden the 655 // newChildBalancer function as part of test setup. 656 cdsUpdate := xdsresource.ClusterUpdate{ClusterName: serviceName} 657 wantCCS := edsCCS(serviceName, nil, false, nil) 658 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 659 defer ctxCancel() 660 if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil { 661 t.Fatal(err) 662 } 663 664 // Close the CDS balancer. 665 cdsB.Close() 666 667 // Make sure that the cluster watch registered by the CDS balancer is 668 // cancelled. 669 if _, err := xdsC.WaitForCancelClusterWatch(ctx); err != nil { 670 t.Fatal(err) 671 } 672 673 // Make sure that a cluster update is not acted upon. 674 xdsC.InvokeWatchClusterCallback(cdsUpdate, nil) 675 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 676 defer sCancel() 677 if err := edsB.waitForClientConnUpdate(sCtx, balancer.ClientConnState{}); err != context.DeadlineExceeded { 678 t.Fatalf("ClusterUpdate after close forwaded to EDS balancer") 679 } 680 681 // Make sure that the underlying EDS balancer is closed. 682 if err := edsB.waitForClose(ctx); err != nil { 683 t.Fatal(err) 684 } 685 686 // Make sure that the UpdateClientConnState() method on the CDS balancer 687 // returns error. 688 if err := cdsB.UpdateClientConnState(cdsCCS(clusterName, xdsC)); err != errBalancerClosed { 689 t.Fatalf("UpdateClientConnState() after close returned %v, want %v", err, errBalancerClosed) 690 } 691 692 // Make sure that the UpdateSubConnState() method on the CDS balancer does 693 // not forward the update to the EDS balancer. 694 cdsB.UpdateSubConnState(&testutils.TestSubConn{}, balancer.SubConnState{}) 695 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 696 defer sCancel() 697 if err := edsB.waitForSubConnUpdate(sCtx, subConnWithState{}); err != context.DeadlineExceeded { 698 t.Fatal("UpdateSubConnState() forwarded to EDS balancer after Close()") 699 } 700 701 // Make sure that the ResolverErr() method on the CDS balancer does not 702 // forward the update to the EDS balancer. 703 rErr := errors.New("cdsBalancer resolver error") 704 cdsB.ResolverError(rErr) 705 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 706 defer sCancel() 707 if err := edsB.waitForResolverError(sCtx, rErr); err != context.DeadlineExceeded { 708 t.Fatal("ResolverError() forwarded to EDS balancer after Close()") 709 } 710 } 711 712 func (s) TestExitIdle(t *testing.T) { 713 // This creates a CDS balancer, pushes a ClientConnState update with a fake 714 // xdsClient, and makes sure that the CDS balancer registers a watch on the 715 // provided xdsClient. 716 xdsC, cdsB, edsB, _, cancel := setupWithWatch(t) 717 defer func() { 718 cancel() 719 cdsB.Close() 720 }() 721 722 // Here we invoke the watch callback registered on the fake xdsClient. This 723 // will trigger the watch handler on the CDS balancer, which will attempt to 724 // create a new EDS balancer. The fake EDS balancer created above will be 725 // returned to the CDS balancer, because we have overridden the 726 // newChildBalancer function as part of test setup. 727 cdsUpdate := xdsresource.ClusterUpdate{ClusterName: serviceName} 728 wantCCS := edsCCS(serviceName, nil, false, nil) 729 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 730 defer ctxCancel() 731 if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil { 732 t.Fatal(err) 733 } 734 735 // Call ExitIdle on the CDS balancer. 736 cdsB.ExitIdle() 737 738 edsB.exitIdleCh.Receive(ctx) 739 } 740 741 // TestParseConfig verifies the ParseConfig() method in the CDS balancer. 742 func (s) TestParseConfig(t *testing.T) { 743 bb := balancer.Get(cdsName) 744 if bb == nil { 745 t.Fatalf("balancer.Get(%q) returned nil", cdsName) 746 } 747 parser, ok := bb.(balancer.ConfigParser) 748 if !ok { 749 t.Fatalf("balancer %q does not implement the ConfigParser interface", cdsName) 750 } 751 752 tests := []struct { 753 name string 754 input json.RawMessage 755 wantCfg serviceconfig.LoadBalancingConfig 756 wantErr bool 757 }{ 758 { 759 name: "good-lb-config", 760 input: json.RawMessage(`{"Cluster": "cluster1"}`), 761 wantCfg: &lbConfig{ClusterName: "cluster1"}, 762 }, 763 { 764 name: "unknown-fields-in-lb-config", 765 input: json.RawMessage(`{"Unknown": "foobar"}`), 766 wantCfg: &lbConfig{ClusterName: ""}, 767 }, 768 { 769 name: "empty-lb-config", 770 input: json.RawMessage(""), 771 wantErr: true, 772 }, 773 } 774 775 for _, test := range tests { 776 t.Run(test.name, func(t *testing.T) { 777 gotCfg, gotErr := parser.ParseConfig(test.input) 778 if (gotErr != nil) != test.wantErr { 779 t.Fatalf("ParseConfig(%v) = %v, wantErr %v", string(test.input), gotErr, test.wantErr) 780 } 781 if test.wantErr { 782 return 783 } 784 if !cmp.Equal(gotCfg, test.wantCfg) { 785 t.Fatalf("ParseConfig(%v) = %v, want %v", string(test.input), gotCfg, test.wantCfg) 786 } 787 }) 788 } 789 }