@@ -51,16 +51,16 @@ import (
5151 testpb "google.golang.org/grpc/interop/grpc_testing"
5252)
5353
54- // interceptingBuilder wraps a resolver.Builder and intercepts the ClientConn
54+ // wrappingBuilder wraps a resolver.Builder and intercepts the ClientConn
5555// passed to Build by wrapping it with an interceptingClientConn.
56- type interceptingBuilder struct {
56+ type wrappingBuilder struct {
5757 resolver.Builder
5858 jsonCh chan string
5959}
6060
6161// Build wraps the provided resolver.ClientConn with an interceptingClientConn
6262// before delegating to the underlying resolver.Builder.
63- func (ib * interceptingBuilder ) Build (target resolver.Target , cc resolver.ClientConn , opts resolver.BuildOptions ) (resolver.Resolver , error ) {
63+ func (ib * wrappingBuilder ) Build (target resolver.Target , cc resolver.ClientConn , opts resolver.BuildOptions ) (resolver.Resolver , error ) {
6464 icc := & interceptingClientConn {
6565 ClientConn : cc ,
6666 jsonCh : ib .jsonCh ,
@@ -149,6 +149,19 @@ func (i *testInterceptor) NewStream(ctx context.Context, _ iresolver.RPCInfo, ne
149149
150150func (i * testInterceptor ) Close () {}
151151
152+ func newTestFilterBuilder (t testing.TB , typeURL string ) * testFilterBuilder {
153+ tb := & testFilterBuilder {
154+ typeURL : typeURL ,
155+ blockChan : make (chan struct {}),
156+ enteredChan : make (chan struct {}, 1 ),
157+ }
158+ httpfilter .Register (tb )
159+ t .Cleanup (func () {
160+ httpfilter .UnregisterForTesting (typeURL )
161+ })
162+ return tb
163+ }
164+
152165// wantServiceConfig returns a JSON representation of a service config with
153166// xds_cluster_manager_experimental LB policy with child policies of
154167// cds_experimental for the provided cluster names.
@@ -190,19 +203,23 @@ func compareJSONConfigs(t *testing.T, gotJSON, wantJSON string) {
190203 }
191204}
192205
206+ func verifyServiceConfig (ctx context.Context , t * testing.T , jsonCh chan string , wantClusters ... string ) {
207+ t .Helper ()
208+ select {
209+ case <- ctx .Done ():
210+ t .Fatal ("Timeout waiting for resolver state update" )
211+ case js := <- jsonCh :
212+ compareJSONConfigs (t , js , wantServiceConfig (wantClusters ... ))
213+ }
214+ }
215+
193216// Test verifies that when RPCs are in flight holding references to an old
194217// cluster, that cluster remains in the service config until all in-flight
195218// RPCs finish.
196219func (s ) TestResolverDelayedClusterRemoval_MultipleInFlightRPCs (t * testing.T ) {
197220 testFilterTypeURL := t .Name ()
198- blockChan , enteredChan := make (chan struct {}), make (chan struct {}, 2 )
199- tb := & testFilterBuilder {
200- typeURL : testFilterTypeURL ,
201- blockChan : blockChan ,
202- enteredChan : enteredChan ,
203- }
204- httpfilter .Register (tb )
205- defer httpfilter .UnregisterForTesting (tb .typeURL )
221+ tb := newTestFilterBuilder (t , testFilterTypeURL )
222+ blockChan , enteredChan := tb .blockChan , tb .enteredChan
206223
207224 // Start an xDS management server.
208225 mgmtServer := e2e .StartManagementServer (t , e2e.ManagementServerOptions {})
@@ -217,8 +234,8 @@ func (s) TestResolverDelayedClusterRemoval_MultipleInFlightRPCs(t *testing.T) {
217234 }
218235
219236 // Create an intercepting resolver builder.
220- jsonCh := make (chan string , 4 )
221- ib := & interceptingBuilder {
237+ jsonCh := make (chan string , 3 )
238+ ib := & wrappingBuilder {
222239 Builder : r ,
223240 jsonCh : jsonCh ,
224241 }
@@ -286,10 +303,10 @@ func (s) TestResolverDelayedClusterRemoval_MultipleInFlightRPCs(t *testing.T) {
286303
287304 // Trigger first RPC to cluster A. This RPC should reach our custom HTTP
288305 // filter, and get blocked.
289- rpc1Err := make (chan error , 1 )
306+ errCh := make (chan error , 2 )
290307 go func () {
291308 _ , err := client .EmptyCall (ctx , & testpb.Empty {})
292- rpc1Err <- err
309+ errCh <- err
293310 }()
294311
295312 // Verify that first RPC has entered the HTTP filter's NewStream and is
@@ -302,19 +319,13 @@ func (s) TestResolverDelayedClusterRemoval_MultipleInFlightRPCs(t *testing.T) {
302319
303320 // Read the first state update from the intercepting resolver.
304321 // This should contain cluster-A.
305- select {
306- case <- ctx .Done ():
307- t .Fatal ("Timeout waiting for first resolver state update" )
308- case js := <- jsonCh :
309- compareJSONConfigs (t , js , wantServiceConfig (clusterA ))
310- }
322+ verifyServiceConfig (ctx , t , jsonCh , clusterA )
311323
312324 // Trigger second RPC to cluster A. This RPC should reach our custom HTTP
313325 // filter, and get blocked.
314- rpc2Err := make (chan error , 1 )
315326 go func () {
316327 _ , err := client .EmptyCall (ctx , & testpb.Empty {})
317- rpc2Err <- err
328+ errCh <- err
318329 }()
319330
320331 // Verify that second RPC has entered the HTTP filter's NewStream and is
@@ -334,29 +345,17 @@ func (s) TestResolverDelayedClusterRemoval_MultipleInFlightRPCs(t *testing.T) {
334345 t .Fatal (err )
335346 }
336347
337- select {
338- case <- ctx .Done ():
339- t .Fatal ("Timeout waiting for second resolver state update" )
340- case js := <- jsonCh :
341- compareJSONConfigs (t , js , wantServiceConfig (clusterA , clusterB ))
342- }
348+ verifyServiceConfig (ctx , t , jsonCh , clusterA , clusterB )
343349
344350 // Unblock one of the RPCs and verify it completes successfully.
345351 blockChan <- struct {}{}
346- var remainingRPCErr chan error
347352 select {
348353 case <- ctx .Done ():
349354 t .Fatal ("Timeout waiting for an RPC to succeed" )
350- case err := <- rpc1Err :
355+ case err := <- errCh :
351356 if err != nil {
352357 t .Fatalf ("RPC failed with unexpected error: %v" , err )
353358 }
354- remainingRPCErr = rpc2Err
355- case err := <- rpc2Err :
356- if err != nil {
357- t .Fatalf ("RPC failed with unexpected error: %v" , err )
358- }
359- remainingRPCErr = rpc1Err
360359 }
361360
362361 // Verify that because the other RPC to cluster-A is still in flight, the
@@ -373,20 +372,15 @@ func (s) TestResolverDelayedClusterRemoval_MultipleInFlightRPCs(t *testing.T) {
373372 select {
374373 case <- ctx .Done ():
375374 t .Fatal ("Timeout waiting for remaining RPC to succeed" )
376- case err := <- remainingRPCErr :
375+ case err := <- errCh :
377376 if err != nil {
378377 t .Fatalf ("Remaining RPC failed with unexpected error: %v" , err )
379378 }
380379 }
381380
382381 // Once the second RPC finishes (refCount drops 1 -> 0), cluster-A should be
383382 // removed from the service config.
384- select {
385- case <- ctx .Done ():
386- t .Fatal ("Timeout waiting for third resolver state update" )
387- case js := <- jsonCh :
388- compareJSONConfigs (t , js , wantServiceConfig (clusterB ))
389- }
383+ verifyServiceConfig (ctx , t , jsonCh , clusterB )
390384}
391385
392386// Test verifies that if stream creation fails early inside an HTTP filter or
@@ -396,15 +390,9 @@ func (s) TestResolverDelayedClusterRemoval_MultipleInFlightRPCs(t *testing.T) {
396390func (s ) TestResolverPrunesCluster_StreamCreationFailure (t * testing.T ) {
397391 testFilterTypeURL := t .Name ()
398392 const wantErr = "blocking filter error"
399- blockChan , enteredChan := make (chan struct {}), make (chan struct {})
400- tb := & testFilterBuilder {
401- typeURL : testFilterTypeURL ,
402- blockChan : blockChan ,
403- enteredChan : enteredChan ,
404- newStreamErr : status .Error (codes .Unavailable , wantErr ),
405- }
406- httpfilter .Register (tb )
407- defer httpfilter .UnregisterForTesting (tb .typeURL )
393+ tb := newTestFilterBuilder (t , testFilterTypeURL )
394+ tb .newStreamErr = status .Error (codes .Unavailable , wantErr )
395+ close (tb .blockChan ) // Close blockChan immediately so that the RPC fails without blocking
408396
409397 // Start an xDS management server.
410398 mgmtServer := e2e .StartManagementServer (t , e2e.ManagementServerOptions {})
@@ -420,7 +408,7 @@ func (s) TestResolverPrunesCluster_StreamCreationFailure(t *testing.T) {
420408
421409 // Intercept resolver builder.
422410 jsonCh := make (chan string , 3 )
423- ib := & interceptingBuilder {
411+ ib := & wrappingBuilder {
424412 Builder : r ,
425413 jsonCh : jsonCh ,
426414 }
@@ -480,34 +468,20 @@ func (s) TestResolverPrunesCluster_StreamCreationFailure(t *testing.T) {
480468 // Create a gRPC client using the xDS resolver.
481469 cc , err := grpc .NewClient (fmt .Sprintf ("xds:///%s" , serviceName ), grpc .WithTransportCredentials (insecure .NewCredentials ()), grpc .WithResolvers (ib ))
482470 if err != nil {
483- t .Fatalf ("Failed to dial : %v" , err )
471+ t .Fatalf ("Failed to create a gRPC client : %v" , err )
484472 }
485473 defer cc .Close ()
486-
487- // Trigger a RPC to cluster A. This RPC should reach our custom HTTP filter,
488- // and get blocked.
489- client := testgrpc .NewTestServiceClient (cc )
490- rpcErrCh := make (chan error , 1 )
491- go func () {
492- _ , err := client .EmptyCall (ctx , & testpb.Empty {})
493- rpcErrCh <- err
494- }()
474+ cc .Connect ()
495475
496476 // Read the first state update from the intercepting resolver.
497477 // This should contain cluster-A.
498- select {
499- case <- ctx .Done ():
500- t .Fatal ("Timeout waiting for first resolver state update" )
501- case js := <- jsonCh :
502- compareJSONConfigs (t , js , wantServiceConfig (clusterA ))
503- }
478+ verifyServiceConfig (ctx , t , jsonCh , clusterA )
504479
505- // Verify that the RPC has entered the HTTP filter's NewStream and is
506- // currently blocked.
507- select {
508- case <- enteredChan :
509- case <- ctx .Done ():
510- t .Fatal ("Timeout waiting for RPC to reach HTTP filter" )
480+ // Make an RPC. Since the filter immediately fails, this should fail with
481+ // the expected error.
482+ client := testgrpc .NewTestServiceClient (cc )
483+ if _ , err = client .EmptyCall (ctx , & testpb.Empty {}); status .Code (err ) != codes .Unavailable || ! strings .Contains (err .Error (), wantErr ) {
484+ t .Fatalf ("RPC failed with error %v, want code %v with desc containing %q" , err , codes .Unavailable , wantErr )
511485 }
512486
513487 // Update the route configuration on the management server to point
@@ -520,36 +494,12 @@ func (s) TestResolverPrunesCluster_StreamCreationFailure(t *testing.T) {
520494 t .Fatal (err )
521495 }
522496
523- // Read the second state update from the intercepting resolver.
524- // This should contain both cluster-A and cluster-B, since the blocked
525- // RPC holds a reference to cluster-A.
526- select {
527- case <- ctx .Done ():
528- t .Fatal ("Timeout waiting for second resolver state update" )
529- case js := <- jsonCh :
530- compareJSONConfigs (t , js , wantServiceConfig (clusterA , clusterB ))
531- }
532-
533- // Unblock the filter's NewStream.
534- close (blockChan )
497+ // Read the second state update from the intercepting resolver. This should
498+ // contain both cluster-A and cluster-B.
499+ verifyServiceConfig (ctx , t , jsonCh , clusterA , clusterB )
535500
536- // Verify the RPC returns the expected blocking filter error.
537- select {
538- case <- ctx .Done ():
539- t .Fatal ("Timeout waiting for RPC to fail" )
540- case err := <- rpcErrCh :
541- if status .Code (err ) != codes .Unavailable || ! strings .Contains (err .Error (), wantErr ) {
542- t .Fatalf ("RPC failed with error %v, want code %v with desc containing %q" , err , codes .Unavailable , wantErr )
543- }
544- }
545-
546- // Once the stream fails early, the resolver should unsubscribe from
547- // cluster A and trigger a service config update that deletes the old
548- // cluster. Read the third state update and ensure cluster-A is pruned.
549- select {
550- case <- ctx .Done ():
551- t .Fatal ("Timeout waiting for third resolver state update" )
552- case js := <- jsonCh :
553- compareJSONConfigs (t , js , wantServiceConfig (clusterB ))
554- }
501+ // Read the third state update from the intercepting resolver. Cluster-A's
502+ // reference count drops to 0, causing the resolver to unsubscribe and
503+ // prune it.
504+ verifyServiceConfig (ctx , t , jsonCh , clusterB )
555505}
0 commit comments