@@ -728,3 +728,177 @@ func (s) TestFallback_OnStartup_RPCSuccess(t *testing.T) {
728
728
t .Fatal (err )
729
729
}
730
730
}
731
+
732
+ // TestXDSFallback_ThreeServerPromotion verifies that when the primary
733
+ // management server is unavailable, the system attempts to connect to the
734
+ // first fallback server, and if that is also down, to the second fallback
735
+ // server. It also ensures that the system switches back to the first fallback
736
+ // server once it becomes available again, and eventually returns to the
737
+ // primary server when it comes back online, closing connections to the
738
+ // fallback servers accordingly.
739
+ func (s ) TestXDSFallback_ThreeServerPromotion (t * testing.T ) {
740
+ ctx , cancel := context .WithTimeout (context .Background (), defaultFallbackTestTimeout )
741
+ defer cancel ()
742
+
743
+ // Create three listener wrappers for three management servers.
744
+ primaryWrappedLis := testutils .NewListenerWrapper (t , nil )
745
+ primaryLis := testutils .NewRestartableListener (primaryWrappedLis )
746
+
747
+ secondaryWrappedLis := testutils .NewListenerWrapper (t , nil )
748
+ secondaryLis := testutils .NewRestartableListener (secondaryWrappedLis )
749
+
750
+ tertiaryWrappedLis := testutils .NewListenerWrapper (t , nil )
751
+ tertiaryLis := testutils .NewRestartableListener (tertiaryWrappedLis )
752
+
753
+ // Start the three management servers.
754
+ primaryManagementServer := e2e .StartManagementServer (t , e2e.ManagementServerOptions {Listener : primaryLis })
755
+ secondaryManagementServer := e2e .StartManagementServer (t , e2e.ManagementServerOptions {Listener : secondaryLis })
756
+ tertiaryManagementServer := e2e .StartManagementServer (t , e2e.ManagementServerOptions {Listener : tertiaryLis })
757
+
758
+ // Start three test service backends.
759
+ backend1 := stubserver .StartTestService (t , nil )
760
+ defer backend1 .Stop ()
761
+ backend2 := stubserver .StartTestService (t , nil )
762
+ defer backend2 .Stop ()
763
+ backend3 := stubserver .StartTestService (t , nil )
764
+ defer backend3 .Stop ()
765
+
766
+ nodeID := uuid .New ().String ()
767
+ const serviceName = "my-service-fallback-xds"
768
+
769
+ // Configure partial resources on the primary and secondary
770
+ // management servers.
771
+ primaryManagementServer .Update (ctx , e2e.UpdateOptions {
772
+ NodeID : nodeID ,
773
+ Listeners : []* v3listenerpb.Listener {e2e .DefaultClientListener (serviceName , "route-p" )},
774
+ })
775
+ secondaryManagementServer .Update (ctx , e2e.UpdateOptions {
776
+ NodeID : nodeID ,
777
+ Listeners : []* v3listenerpb.Listener {e2e .DefaultClientListener (serviceName , "route-s1" )},
778
+ })
779
+
780
+ // Configure full resources on tertiary management server.
781
+ tertiaryManagementServer .Update (ctx , e2e .DefaultClientResources (e2e.ResourceParams {
782
+ DialTarget : serviceName ,
783
+ NodeID : nodeID ,
784
+ Host : "localhost" ,
785
+ Port : testutils .ParsePort (t , backend3 .Address ),
786
+ SecLevel : e2e .SecurityLevelNone ,
787
+ }))
788
+
789
+ // Create bootstrap configuration for all three management servers.
790
+ bootstrapContents , err := bootstrap .NewContentsForTesting (bootstrap.ConfigOptionsForTesting {
791
+ Servers : []byte (fmt .Sprintf (`[
792
+ {
793
+ "server_uri": %q,
794
+ "channel_creds": [{"type": "insecure"}]
795
+ },
796
+ {
797
+ "server_uri": %q,
798
+ "channel_creds": [{"type": "insecure"}]
799
+ },
800
+ {
801
+ "server_uri": %q,
802
+ "channel_creds": [{"type": "insecure"}]
803
+ }
804
+ ]` , primaryManagementServer .Address , secondaryManagementServer .Address , tertiaryManagementServer .Address )),
805
+ Node : []byte (fmt .Sprintf (`{"id": "%s"}` , nodeID )),
806
+ })
807
+ if err != nil {
808
+ t .Fatalf ("Failed to create bootstrap file: %v" , err )
809
+ }
810
+
811
+ // Create an xDS client with the above bootstrap configuration.
812
+ config , err := bootstrap .NewConfigFromContents (bootstrapContents )
813
+ if err != nil {
814
+ t .Fatalf ("Failed to parse bootstrap contents: %v" , err )
815
+ }
816
+ pool := xdsclient .NewPool (config )
817
+ if err != nil {
818
+ t .Fatalf ("Failed to create xDS client: %v" , err )
819
+ }
820
+
821
+ // Get the xDS resolver to use the above xDS client.
822
+ resolverBuilder := internal .NewXDSResolverWithPoolForTesting .(func (* xdsclient.Pool ) (resolver.Builder , error ))
823
+ resolver , err := resolverBuilder (pool )
824
+ if err != nil {
825
+ t .Fatalf ("Failed to create xDS resolver for testing: %v" , err )
826
+ }
827
+
828
+ // Start a gRPC client that uses the above xDS resolver.
829
+ cc , err := grpc .NewClient (fmt .Sprintf ("xds:///%s" , serviceName ), grpc .WithTransportCredentials (insecure .NewCredentials ()), grpc .WithResolvers (resolver ))
830
+ if err != nil {
831
+ t .Fatalf ("Failed to create gRPC client: %v" , err )
832
+ }
833
+ defer cc .Close ()
834
+ cc .Connect ()
835
+ client := testgrpc .NewTestServiceClient (cc )
836
+
837
+ // Verify that connection attempts were made to primaryWrappedLis and
838
+ // secondaryWrappedLis, before using tertiaryWrappedLis to make
839
+ // successful RPCs to backend3.
840
+ if _ , err := primaryWrappedLis .NewConnCh .Receive (ctx ); err != nil {
841
+ t .Fatalf ("Timeout when waiting for connection to primary: %v" , err )
842
+ }
843
+
844
+ // Stop primary, client should connect to secondary.
845
+ primaryLis .Stop ()
846
+ if _ , err := secondaryWrappedLis .NewConnCh .Receive (ctx ); err != nil {
847
+ t .Fatalf ("Timeout when waiting for connection to secondary after primary stopped: %v" , err )
848
+ }
849
+
850
+ // Stop secondary, client should connect to tertiary.
851
+ secondaryLis .Stop ()
852
+ tertiaryConn , err := tertiaryWrappedLis .NewConnCh .Receive (ctx )
853
+ if err != nil {
854
+ t .Fatalf ("Timeout when waiting for connection to tertiary after secondary stopped: %v" , err )
855
+ }
856
+
857
+ // Tertiary has all resources, RPCs should succeed to backend3.
858
+ if err := waitForRPCsToReachBackend (ctx , client , backend3 .Address ); err != nil {
859
+ t .Fatal (err )
860
+ }
861
+
862
+ // Secondary1 becomes available, RPCs go to backend2.
863
+ secondaryLis .Restart ()
864
+ secondaryManagementServer .Update (ctx , e2e.UpdateOptions {
865
+ NodeID : nodeID ,
866
+ Listeners : []* v3listenerpb.Listener {e2e .DefaultClientListener (serviceName , "route-s1" )},
867
+ Routes : []* v3routepb.RouteConfiguration {e2e .DefaultRouteConfig ("route-s1" , serviceName , "cluster-s1" )},
868
+ Clusters : []* v3clusterpb.Cluster {e2e .DefaultCluster ("cluster-s1" , "endpoints-s1" , e2e .SecurityLevelNone )},
869
+ Endpoints : []* v3endpointpb.ClusterLoadAssignment {
870
+ e2e .DefaultEndpoint ("endpoints-s1" , "localhost" , []uint32 {testutils .ParsePort (t , backend2 .Address )}),
871
+ },
872
+ })
873
+
874
+ secondaryConn , err := secondaryWrappedLis .NewConnCh .Receive (ctx )
875
+ if err != nil {
876
+ t .Fatalf ("Timeout when waiting for new connection to secondary: %v" , err )
877
+ }
878
+ if _ , err := tertiaryConn .(* testutils.ConnWrapper ).CloseCh .Receive (ctx ); err != nil {
879
+ t .Fatalf ("Timeout when waiting for connection to the tertiary to be closed after promotion to secondary: %v" , err )
880
+ }
881
+ if err := waitForRPCsToReachBackend (ctx , client , backend2 .Address ); err != nil {
882
+ t .Fatal (err )
883
+ }
884
+
885
+ // Primary becomes available, RPCs go to backend1.
886
+ primaryLis .Restart ()
887
+ primaryManagementServer .Update (ctx , e2e .DefaultClientResources (e2e.ResourceParams {
888
+ DialTarget : serviceName ,
889
+ NodeID : nodeID ,
890
+ Host : "localhost" ,
891
+ Port : testutils .ParsePort (t , backend1 .Address ),
892
+ SecLevel : e2e .SecurityLevelNone ,
893
+ }))
894
+
895
+ if _ , err := primaryWrappedLis .NewConnCh .Receive (ctx ); err != nil {
896
+ t .Fatalf ("Timeout when waiting for new connection to primary: %v" , err )
897
+ }
898
+ if _ , err := secondaryConn .(* testutils.ConnWrapper ).CloseCh .Receive (ctx ); err != nil {
899
+ t .Fatalf ("Timeout when waiting for connection to the secondary to be closed after promotion to primary: %v" , err )
900
+ }
901
+ if err := waitForRPCsToReachBackend (ctx , client , backend1 .Address ); err != nil {
902
+ t .Fatal (err )
903
+ }
904
+ }
0 commit comments