github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/clusterresolver/clusterresolver_test.go (about) 1 /* 2 * 3 * Copyright 2019 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 "context" 23 "fmt" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/hxx258456/ccgo/grpc/balancer" 29 "github.com/hxx258456/ccgo/grpc/connectivity" 30 "github.com/hxx258456/ccgo/grpc/internal/grpctest" 31 "github.com/hxx258456/ccgo/grpc/internal/testutils" 32 "github.com/hxx258456/ccgo/grpc/resolver" 33 "github.com/hxx258456/ccgo/grpc/xds/internal" 34 "github.com/hxx258456/ccgo/grpc/xds/internal/testutils/fakeclient" 35 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient" 36 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource" 37 38 _ "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/controller/version/v2" // V2 client registration. 39 ) 40 41 const ( 42 defaultTestTimeout = 1 * time.Second 43 defaultTestShortTimeout = 10 * time.Millisecond 44 testEDSServcie = "test-eds-service-name" 45 testClusterName = "test-cluster-name" 46 ) 47 48 var ( 49 // A non-empty endpoints update which is expected to be accepted by the EDS 50 // LB policy. 51 defaultEndpointsUpdate = xdsresource.EndpointsUpdate{ 52 Localities: []xdsresource.Locality{ 53 { 54 Endpoints: []xdsresource.Endpoint{{Address: "endpoint1"}}, 55 ID: internal.LocalityID{Zone: "zone"}, 56 Priority: 1, 57 Weight: 100, 58 }, 59 }, 60 } 61 ) 62 63 func init() { 64 balancer.Register(bb{}) 65 } 66 67 type s struct { 68 grpctest.Tester 69 70 cleanup func() 71 } 72 73 func (ss s) Teardown(t *testing.T) { 74 xdsclient.ClearAllCountersForTesting() 75 ss.Tester.Teardown(t) 76 if ss.cleanup != nil { 77 ss.cleanup() 78 } 79 } 80 81 func Test(t *testing.T) { 82 grpctest.RunSubTests(t, s{}) 83 } 84 85 const testBalancerNameFooBar = "foo.bar" 86 87 func newNoopTestClientConn() *noopTestClientConn { 88 return &noopTestClientConn{} 89 } 90 91 // noopTestClientConn is used in EDS balancer config update tests that only 92 // cover the config update handling, but not SubConn/load-balancing. 93 type noopTestClientConn struct { 94 balancer.ClientConn 95 } 96 97 func (t *noopTestClientConn) NewSubConn([]resolver.Address, balancer.NewSubConnOptions) (balancer.SubConn, error) { 98 return nil, nil 99 } 100 101 func (noopTestClientConn) Target() string { return testEDSServcie } 102 103 type scStateChange struct { 104 sc balancer.SubConn 105 state balancer.SubConnState 106 } 107 108 type fakeChildBalancer struct { 109 cc balancer.ClientConn 110 subConnState *testutils.Channel 111 clientConnState *testutils.Channel 112 resolverError *testutils.Channel 113 } 114 115 func (f *fakeChildBalancer) UpdateClientConnState(state balancer.ClientConnState) error { 116 f.clientConnState.Send(state) 117 return nil 118 } 119 120 func (f *fakeChildBalancer) ResolverError(err error) { 121 f.resolverError.Send(err) 122 } 123 124 func (f *fakeChildBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) { 125 f.subConnState.Send(&scStateChange{sc: sc, state: state}) 126 } 127 128 func (f *fakeChildBalancer) Close() {} 129 130 func (f *fakeChildBalancer) ExitIdle() {} 131 132 func (f *fakeChildBalancer) waitForClientConnStateChange(ctx context.Context) error { 133 _, err := f.clientConnState.Receive(ctx) 134 if err != nil { 135 return err 136 } 137 return nil 138 } 139 140 func (f *fakeChildBalancer) waitForResolverError(ctx context.Context) error { 141 _, err := f.resolverError.Receive(ctx) 142 if err != nil { 143 return err 144 } 145 return nil 146 } 147 148 func (f *fakeChildBalancer) waitForSubConnStateChange(ctx context.Context, wantState *scStateChange) error { 149 val, err := f.subConnState.Receive(ctx) 150 if err != nil { 151 return err 152 } 153 gotState := val.(*scStateChange) 154 if !cmp.Equal(gotState, wantState, cmp.AllowUnexported(scStateChange{})) { 155 return fmt.Errorf("got subconnStateChange %v, want %v", gotState, wantState) 156 } 157 return nil 158 } 159 160 func newFakeChildBalancer(cc balancer.ClientConn) balancer.Balancer { 161 return &fakeChildBalancer{ 162 cc: cc, 163 subConnState: testutils.NewChannelWithSize(10), 164 clientConnState: testutils.NewChannelWithSize(10), 165 resolverError: testutils.NewChannelWithSize(10), 166 } 167 } 168 169 type fakeSubConn struct{} 170 171 func (*fakeSubConn) UpdateAddresses([]resolver.Address) { panic("implement me") } 172 func (*fakeSubConn) Connect() { panic("implement me") } 173 174 // waitForNewChildLB makes sure that a new child LB is created by the top-level 175 // clusterResolverBalancer. 176 func waitForNewChildLB(ctx context.Context, ch *testutils.Channel) (*fakeChildBalancer, error) { 177 val, err := ch.Receive(ctx) 178 if err != nil { 179 return nil, fmt.Errorf("error when waiting for a new edsLB: %v", err) 180 } 181 return val.(*fakeChildBalancer), nil 182 } 183 184 // setup overrides the functions which are used to create the xdsClient and the 185 // edsLB, creates fake version of them and makes them available on the provided 186 // channels. The returned cancel function should be called by the test for 187 // cleanup. 188 func setup(childLBCh *testutils.Channel) (*fakeclient.Client, func()) { 189 xdsC := fakeclient.NewClientWithName(testBalancerNameFooBar) 190 191 origNewChildBalancer := newChildBalancer 192 newChildBalancer = func(_ balancer.Builder, cc balancer.ClientConn, _ balancer.BuildOptions) balancer.Balancer { 193 childLB := newFakeChildBalancer(cc) 194 defer func() { childLBCh.Send(childLB) }() 195 return childLB 196 } 197 return xdsC, func() { 198 newChildBalancer = origNewChildBalancer 199 xdsC.Close() 200 } 201 } 202 203 // TestSubConnStateChange verifies if the top-level clusterResolverBalancer passes on 204 // the subConnState to appropriate child balancer. 205 func (s) TestSubConnStateChange(t *testing.T) { 206 edsLBCh := testutils.NewChannel() 207 xdsC, cleanup := setup(edsLBCh) 208 defer cleanup() 209 210 builder := balancer.Get(Name) 211 edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{}) 212 if edsB == nil { 213 t.Fatalf("builder.Build(%s) failed and returned nil", Name) 214 } 215 defer edsB.Close() 216 217 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 218 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 219 BalancerConfig: newLBConfigWithOneEDS(testEDSServcie), 220 }); err != nil { 221 t.Fatalf("edsB.UpdateClientConnState() failed: %v", err) 222 } 223 224 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 225 defer cancel() 226 if _, err := xdsC.WaitForWatchEDS(ctx); err != nil { 227 t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err) 228 } 229 xdsC.InvokeWatchEDSCallback("", defaultEndpointsUpdate, nil) 230 edsLB, err := waitForNewChildLB(ctx, edsLBCh) 231 if err != nil { 232 t.Fatal(err) 233 } 234 235 fsc := &fakeSubConn{} 236 state := balancer.SubConnState{ConnectivityState: connectivity.Ready} 237 edsB.UpdateSubConnState(fsc, state) 238 if err := edsLB.waitForSubConnStateChange(ctx, &scStateChange{sc: fsc, state: state}); err != nil { 239 t.Fatal(err) 240 } 241 } 242 243 // TestErrorFromXDSClientUpdate verifies that an error from xdsClient update is 244 // handled correctly. 245 // 246 // If it's resource-not-found, watch will NOT be canceled, the EDS impl will 247 // receive an empty EDS update, and new RPCs will fail. 248 // 249 // If it's connection error, nothing will happen. This will need to change to 250 // handle fallback. 251 func (s) TestErrorFromXDSClientUpdate(t *testing.T) { 252 edsLBCh := testutils.NewChannel() 253 xdsC, cleanup := setup(edsLBCh) 254 defer cleanup() 255 256 builder := balancer.Get(Name) 257 edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{}) 258 if edsB == nil { 259 t.Fatalf("builder.Build(%s) failed and returned nil", Name) 260 } 261 defer edsB.Close() 262 263 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 264 defer cancel() 265 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 266 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 267 BalancerConfig: newLBConfigWithOneEDS(testEDSServcie), 268 }); err != nil { 269 t.Fatal(err) 270 } 271 if _, err := xdsC.WaitForWatchEDS(ctx); err != nil { 272 t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err) 273 } 274 xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, nil) 275 edsLB, err := waitForNewChildLB(ctx, edsLBCh) 276 if err != nil { 277 t.Fatal(err) 278 } 279 if err := edsLB.waitForClientConnStateChange(ctx); err != nil { 280 t.Fatalf("EDS impl got unexpected update: %v", err) 281 } 282 283 connectionErr := xdsresource.NewErrorf(xdsresource.ErrorTypeConnection, "connection error") 284 xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, connectionErr) 285 286 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 287 defer sCancel() 288 if _, err := xdsC.WaitForCancelEDSWatch(sCtx); err != context.DeadlineExceeded { 289 t.Fatal("watch was canceled, want not canceled (timeout error)") 290 } 291 292 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 293 defer sCancel() 294 if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded { 295 t.Fatal(err) 296 } 297 if err := edsLB.waitForResolverError(ctx); err != nil { 298 t.Fatalf("want resolver error, got %v", err) 299 } 300 301 resourceErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "clusterResolverBalancer resource not found error") 302 xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, resourceErr) 303 // Even if error is resource not found, watch shouldn't be canceled, because 304 // this is an EDS resource removed (and xds client actually never sends this 305 // error, but we still handles it). 306 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 307 defer sCancel() 308 if _, err := xdsC.WaitForCancelEDSWatch(sCtx); err != context.DeadlineExceeded { 309 t.Fatal("watch was canceled, want not canceled (timeout error)") 310 } 311 if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded { 312 t.Fatal(err) 313 } 314 if err := edsLB.waitForResolverError(ctx); err != nil { 315 t.Fatalf("want resolver error, got %v", err) 316 } 317 318 // An update with the same service name should not trigger a new watch. 319 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 320 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 321 BalancerConfig: newLBConfigWithOneEDS(testEDSServcie), 322 }); err != nil { 323 t.Fatal(err) 324 } 325 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 326 defer sCancel() 327 if _, err := xdsC.WaitForWatchEDS(sCtx); err != context.DeadlineExceeded { 328 t.Fatal("got unexpected new EDS watch") 329 } 330 } 331 332 // TestErrorFromResolver verifies that resolver errors are handled correctly. 333 // 334 // If it's resource-not-found, watch will be canceled, the EDS impl will receive 335 // an empty EDS update, and new RPCs will fail. 336 // 337 // If it's connection error, nothing will happen. This will need to change to 338 // handle fallback. 339 func (s) TestErrorFromResolver(t *testing.T) { 340 edsLBCh := testutils.NewChannel() 341 xdsC, cleanup := setup(edsLBCh) 342 defer cleanup() 343 344 builder := balancer.Get(Name) 345 edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{}) 346 if edsB == nil { 347 t.Fatalf("builder.Build(%s) failed and returned nil", Name) 348 } 349 defer edsB.Close() 350 351 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 352 defer cancel() 353 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 354 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 355 BalancerConfig: newLBConfigWithOneEDS(testEDSServcie), 356 }); err != nil { 357 t.Fatal(err) 358 } 359 360 if _, err := xdsC.WaitForWatchEDS(ctx); err != nil { 361 t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err) 362 } 363 xdsC.InvokeWatchEDSCallback("", xdsresource.EndpointsUpdate{}, nil) 364 edsLB, err := waitForNewChildLB(ctx, edsLBCh) 365 if err != nil { 366 t.Fatal(err) 367 } 368 if err := edsLB.waitForClientConnStateChange(ctx); err != nil { 369 t.Fatalf("EDS impl got unexpected update: %v", err) 370 } 371 372 connectionErr := xdsresource.NewErrorf(xdsresource.ErrorTypeConnection, "connection error") 373 edsB.ResolverError(connectionErr) 374 375 sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 376 defer sCancel() 377 if _, err := xdsC.WaitForCancelEDSWatch(sCtx); err != context.DeadlineExceeded { 378 t.Fatal("watch was canceled, want not canceled (timeout error)") 379 } 380 381 sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout) 382 defer sCancel() 383 if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded { 384 t.Fatal("eds impl got EDS resp, want timeout error") 385 } 386 if err := edsLB.waitForResolverError(ctx); err != nil { 387 t.Fatalf("want resolver error, got %v", err) 388 } 389 390 resourceErr := xdsresource.NewErrorf(xdsresource.ErrorTypeResourceNotFound, "clusterResolverBalancer resource not found error") 391 edsB.ResolverError(resourceErr) 392 if _, err := xdsC.WaitForCancelEDSWatch(ctx); err != nil { 393 t.Fatalf("want watch to be canceled, waitForCancel failed: %v", err) 394 } 395 if err := edsLB.waitForClientConnStateChange(sCtx); err != context.DeadlineExceeded { 396 t.Fatal(err) 397 } 398 if err := edsLB.waitForResolverError(ctx); err != nil { 399 t.Fatalf("want resolver error, got %v", err) 400 } 401 402 // An update with the same service name should trigger a new watch, because 403 // the previous watch was canceled. 404 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 405 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 406 BalancerConfig: newLBConfigWithOneEDS(testEDSServcie), 407 }); err != nil { 408 t.Fatal(err) 409 } 410 if _, err := xdsC.WaitForWatchEDS(ctx); err != nil { 411 t.Fatalf("xdsClient.WatchEndpoints failed with error: %v", err) 412 } 413 } 414 415 // Given a list of resource names, verifies that EDS requests for the same are 416 // sent by the EDS balancer, through the fake xDS client. 417 func verifyExpectedRequests(ctx context.Context, fc *fakeclient.Client, resourceNames ...string) error { 418 for _, name := range resourceNames { 419 if name == "" { 420 // ResourceName empty string indicates a cancel. 421 if _, err := fc.WaitForCancelEDSWatch(ctx); err != nil { 422 return fmt.Errorf("timed out when expecting resource %q", name) 423 } 424 continue 425 } 426 427 resName, err := fc.WaitForWatchEDS(ctx) 428 if err != nil { 429 return fmt.Errorf("timed out when expecting resource %q, %p", name, fc) 430 } 431 if resName != name { 432 return fmt.Errorf("got EDS request for resource %q, expected: %q", resName, name) 433 } 434 } 435 return nil 436 } 437 438 // TestClientWatchEDS verifies that the xdsClient inside the top-level EDS LB 439 // policy registers an EDS watch for expected resource upon receiving an update 440 // from gRPC. 441 func (s) TestClientWatchEDS(t *testing.T) { 442 edsLBCh := testutils.NewChannel() 443 xdsC, cleanup := setup(edsLBCh) 444 defer cleanup() 445 446 builder := balancer.Get(Name) 447 edsB := builder.Build(newNoopTestClientConn(), balancer.BuildOptions{}) 448 if edsB == nil { 449 t.Fatalf("builder.Build(%s) failed and returned nil", Name) 450 } 451 defer edsB.Close() 452 453 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 454 defer cancel() 455 // If eds service name is not set, should watch for cluster name. 456 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 457 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 458 BalancerConfig: newLBConfigWithOneEDS("cluster-1"), 459 }); err != nil { 460 t.Fatal(err) 461 } 462 if err := verifyExpectedRequests(ctx, xdsC, "cluster-1"); err != nil { 463 t.Fatal(err) 464 } 465 466 // Update with an non-empty edsServiceName should trigger an EDS watch for 467 // the same. 468 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 469 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 470 BalancerConfig: newLBConfigWithOneEDS("foobar-1"), 471 }); err != nil { 472 t.Fatal(err) 473 } 474 if err := verifyExpectedRequests(ctx, xdsC, "", "foobar-1"); err != nil { 475 t.Fatal(err) 476 } 477 478 // Also test the case where the edsServerName changes from one non-empty 479 // name to another, and make sure a new watch is registered. The previously 480 // registered watch will be cancelled, which will result in an EDS request 481 // with no resource names being sent to the server. 482 if err := edsB.UpdateClientConnState(balancer.ClientConnState{ 483 ResolverState: xdsclient.SetClient(resolver.State{}, xdsC), 484 BalancerConfig: newLBConfigWithOneEDS("foobar-2"), 485 }); err != nil { 486 t.Fatal(err) 487 } 488 if err := verifyExpectedRequests(ctx, xdsC, "", "foobar-2"); err != nil { 489 t.Fatal(err) 490 } 491 } 492 493 func newLBConfigWithOneEDS(edsServiceName string) *LBConfig { 494 return &LBConfig{ 495 DiscoveryMechanisms: []DiscoveryMechanism{{ 496 Cluster: testClusterName, 497 Type: DiscoveryMechanismTypeEDS, 498 EDSServiceName: edsServiceName, 499 }}, 500 } 501 }