Skip to content

Commit 4375c78

Browse files
xdsclient: add an e2e style test for fallback involving more than 2 servers #7817 (#8427)
Fixes: #7817
1 parent 8292549 commit 4375c78

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

xds/internal/xdsclient/tests/fallback_test.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,3 +728,177 @@ func (s) TestFallback_OnStartup_RPCSuccess(t *testing.T) {
728728
t.Fatal(err)
729729
}
730730
}
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

Comments
 (0)