github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/test/xds_server_serving_mode_test.go (about) 1 //go:build !386 2 // +build !386 3 4 /* 5 * 6 * Copyright 2021 gRPC authors. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ 21 22 // Package xds_test contains e2e tests for xDS use. 23 package xds_test 24 25 import ( 26 "context" 27 "fmt" 28 "net" 29 "testing" 30 "time" 31 32 grpc "github.com/hxx258456/ccgo/grpc" 33 "github.com/hxx258456/ccgo/grpc/connectivity" 34 "github.com/hxx258456/ccgo/grpc/credentials/insecure" 35 xdscreds "github.com/hxx258456/ccgo/grpc/credentials/xds" 36 "github.com/hxx258456/ccgo/grpc/internal/testutils" 37 "github.com/hxx258456/ccgo/grpc/xds" 38 "github.com/hxx258456/ccgo/grpc/xds/internal/testutils/e2e" 39 40 v3listenerpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/listener/v3" 41 testpb "github.com/hxx258456/ccgo/grpc/test/grpc_testing" 42 ) 43 44 // TestServerSideXDS_RedundantUpdateSuppression tests the scenario where the 45 // control plane sends the same resource update. It verifies that the mode 46 // change callback is not invoked and client connections to the server are not 47 // recycled. 48 func (s) TestServerSideXDS_RedundantUpdateSuppression(t *testing.T) { 49 managementServer, nodeID, bootstrapContents, _, cleanup := setupManagementServer(t) 50 defer cleanup() 51 52 creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{FallbackCreds: insecure.NewCredentials()}) 53 if err != nil { 54 t.Fatal(err) 55 } 56 lis, err := testutils.LocalTCPListener() 57 if err != nil { 58 t.Fatalf("testutils.LocalTCPListener() failed: %v", err) 59 } 60 updateCh := make(chan connectivity.ServingMode, 1) 61 62 // Create a server option to get notified about serving mode changes. 63 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 64 defer cancel() 65 modeChangeOpt := xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) { 66 t.Logf("serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err) 67 updateCh <- args.Mode 68 }) 69 70 // Initialize an xDS-enabled gRPC server and register the stubServer on it. 71 server := xds.NewGRPCServer(grpc.Creds(creds), modeChangeOpt, xds.BootstrapContentsForTesting(bootstrapContents)) 72 defer server.Stop() 73 testpb.RegisterTestServiceServer(server, &testService{}) 74 75 // Setup the management server to respond with the listener resources. 76 host, port, err := hostPortFromListener(lis) 77 if err != nil { 78 t.Fatalf("failed to retrieve host and port of server: %v", err) 79 } 80 listener := e2e.DefaultServerListener(host, port, e2e.SecurityLevelNone) 81 resources := e2e.UpdateOptions{ 82 NodeID: nodeID, 83 Listeners: []*v3listenerpb.Listener{listener}, 84 } 85 if err := managementServer.Update(ctx, resources); err != nil { 86 t.Fatal(err) 87 } 88 89 go func() { 90 if err := server.Serve(lis); err != nil { 91 t.Errorf("Serve() failed: %v", err) 92 } 93 }() 94 95 // Wait for the listener to move to "serving" mode. 96 select { 97 case <-ctx.Done(): 98 t.Fatalf("timed out waiting for a mode change update: %v", err) 99 case mode := <-updateCh: 100 if mode != connectivity.ServingModeServing { 101 t.Fatalf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing) 102 } 103 } 104 105 // Create a ClientConn and make a successful RPCs. 106 cc, err := grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) 107 if err != nil { 108 t.Fatalf("failed to dial local test server: %v", err) 109 } 110 defer cc.Close() 111 waitForSuccessfulRPC(ctx, t, cc) 112 113 // Start a goroutine to make sure that we do not see any connectivity state 114 // changes on the client connection. If redundant updates are not 115 // suppressed, server will recycle client connections. 116 errCh := make(chan error, 1) 117 go func() { 118 if cc.WaitForStateChange(ctx, connectivity.Ready) { 119 errCh <- fmt.Errorf("unexpected connectivity state change {%s --> %s} on the client connection", connectivity.Ready, cc.GetState()) 120 return 121 } 122 errCh <- nil 123 }() 124 125 // Update the management server with the same listener resource. This will 126 // update the resource version though, and should result in a the management 127 // server sending the same resource to the xDS-enabled gRPC server. 128 if err := managementServer.Update(ctx, e2e.UpdateOptions{ 129 NodeID: nodeID, 130 Listeners: []*v3listenerpb.Listener{listener}, 131 }); err != nil { 132 t.Fatal(err) 133 } 134 135 // Since redundant resource updates are suppressed, we should not see the 136 // mode change callback being invoked. 137 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 138 defer sCancel() 139 select { 140 case <-sCtx.Done(): 141 case mode := <-updateCh: 142 t.Fatalf("unexpected mode change callback with new mode %v", mode) 143 } 144 145 // Make sure RPCs continue to succeed. 146 waitForSuccessfulRPC(ctx, t, cc) 147 148 // Cancel the context to ensure that the WaitForStateChange call exits early 149 // and returns false. 150 cancel() 151 if err := <-errCh; err != nil { 152 t.Fatal(err) 153 } 154 } 155 156 // TestServerSideXDS_ServingModeChanges tests the serving mode functionality in 157 // xDS enabled gRPC servers. It verifies that appropriate mode changes happen in 158 // the server, and also verifies behavior of clientConns under these modes. 159 func (s) TestServerSideXDS_ServingModeChanges(t *testing.T) { 160 managementServer, nodeID, bootstrapContents, _, cleanup := setupManagementServer(t) 161 defer cleanup() 162 163 // Configure xDS credentials to be used on the server-side. 164 creds, err := xdscreds.NewServerCredentials(xdscreds.ServerOptions{ 165 FallbackCreds: insecure.NewCredentials(), 166 }) 167 if err != nil { 168 t.Fatal(err) 169 } 170 171 // Create two local listeners and pass it to Serve(). 172 lis1, err := testutils.LocalTCPListener() 173 if err != nil { 174 t.Fatalf("testutils.LocalTCPListener() failed: %v", err) 175 } 176 lis2, err := testutils.LocalTCPListener() 177 if err != nil { 178 t.Fatalf("testutils.LocalTCPListener() failed: %v", err) 179 } 180 181 // Create a couple of channels on which mode updates will be pushed. 182 updateCh1 := make(chan connectivity.ServingMode, 1) 183 updateCh2 := make(chan connectivity.ServingMode, 1) 184 185 // Create a server option to get notified about serving mode changes, and 186 // push the updated mode on the channels created above. 187 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 188 defer cancel() 189 modeChangeOpt := xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) { 190 t.Logf("serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err) 191 switch addr.String() { 192 case lis1.Addr().String(): 193 updateCh1 <- args.Mode 194 case lis2.Addr().String(): 195 updateCh2 <- args.Mode 196 default: 197 t.Logf("serving mode callback invoked for unknown listener address: %q", addr.String()) 198 } 199 }) 200 201 // Initialize an xDS-enabled gRPC server and register the stubServer on it. 202 server := xds.NewGRPCServer(grpc.Creds(creds), modeChangeOpt, xds.BootstrapContentsForTesting(bootstrapContents)) 203 defer server.Stop() 204 testpb.RegisterTestServiceServer(server, &testService{}) 205 206 // Setup the management server to respond with server-side Listener 207 // resources for both listeners. 208 host1, port1, err := hostPortFromListener(lis1) 209 if err != nil { 210 t.Fatalf("failed to retrieve host and port of server: %v", err) 211 } 212 listener1 := e2e.DefaultServerListener(host1, port1, e2e.SecurityLevelNone) 213 host2, port2, err := hostPortFromListener(lis2) 214 if err != nil { 215 t.Fatalf("failed to retrieve host and port of server: %v", err) 216 } 217 listener2 := e2e.DefaultServerListener(host2, port2, e2e.SecurityLevelNone) 218 resources := e2e.UpdateOptions{ 219 NodeID: nodeID, 220 Listeners: []*v3listenerpb.Listener{listener1, listener2}, 221 } 222 if err := managementServer.Update(ctx, resources); err != nil { 223 t.Fatal(err) 224 } 225 226 go func() { 227 if err := server.Serve(lis1); err != nil { 228 t.Errorf("Serve() failed: %v", err) 229 } 230 }() 231 go func() { 232 if err := server.Serve(lis2); err != nil { 233 t.Errorf("Serve() failed: %v", err) 234 } 235 }() 236 237 // Wait for both listeners to move to "serving" mode. 238 select { 239 case <-ctx.Done(): 240 t.Fatalf("timed out waiting for a mode change update: %v", err) 241 case mode := <-updateCh1: 242 if mode != connectivity.ServingModeServing { 243 t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing) 244 } 245 } 246 select { 247 case <-ctx.Done(): 248 t.Fatalf("timed out waiting for a mode change update: %v", err) 249 case mode := <-updateCh2: 250 if mode != connectivity.ServingModeServing { 251 t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing) 252 } 253 } 254 255 // Create a ClientConn to the first listener and make a successful RPCs. 256 cc1, err := grpc.Dial(lis1.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) 257 if err != nil { 258 t.Fatalf("failed to dial local test server: %v", err) 259 } 260 defer cc1.Close() 261 waitForSuccessfulRPC(ctx, t, cc1) 262 263 // Create a ClientConn to the second listener and make a successful RPCs. 264 cc2, err := grpc.Dial(lis2.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) 265 if err != nil { 266 t.Fatalf("failed to dial local test server: %v", err) 267 } 268 defer cc2.Close() 269 waitForSuccessfulRPC(ctx, t, cc2) 270 271 // Update the management server to remove the second listener resource. This 272 // should push only the second listener into "not-serving" mode. 273 if err := managementServer.Update(ctx, e2e.UpdateOptions{ 274 NodeID: nodeID, 275 Listeners: []*v3listenerpb.Listener{listener1}, 276 }); err != nil { 277 t.Error(err) 278 } 279 280 // Wait for lis2 to move to "not-serving" mode. 281 select { 282 case <-ctx.Done(): 283 t.Fatalf("timed out waiting for a mode change update: %v", err) 284 case mode := <-updateCh2: 285 if mode != connectivity.ServingModeNotServing { 286 t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeNotServing) 287 } 288 } 289 290 // Make sure RPCs succeed on cc1 and fail on cc2. 291 waitForSuccessfulRPC(ctx, t, cc1) 292 waitForFailedRPC(ctx, t, cc2) 293 294 // Update the management server to remove the first listener resource as 295 // well. This should push the first listener into "not-serving" mode. Second 296 // listener is already in "not-serving" mode. 297 if err := managementServer.Update(ctx, e2e.UpdateOptions{ 298 NodeID: nodeID, 299 Listeners: []*v3listenerpb.Listener{}, 300 }); err != nil { 301 t.Error(err) 302 } 303 304 // Wait for lis1 to move to "not-serving" mode. lis2 was already removed 305 // from the xdsclient's resource cache. So, lis2's callback will not be 306 // invoked this time around. 307 select { 308 case <-ctx.Done(): 309 t.Fatalf("timed out waiting for a mode change update: %v", err) 310 case mode := <-updateCh1: 311 if mode != connectivity.ServingModeNotServing { 312 t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeNotServing) 313 } 314 } 315 316 // Make sure RPCs fail on both. 317 waitForFailedRPC(ctx, t, cc1) 318 waitForFailedRPC(ctx, t, cc2) 319 320 // Make sure new connection attempts to "not-serving" servers fail. We use a 321 // short timeout since we expect this to fail. 322 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 323 defer sCancel() 324 if _, err := grpc.DialContext(sCtx, lis1.Addr().String(), grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials())); err == nil { 325 t.Fatal("successfully created clientConn to a server in \"not-serving\" state") 326 } 327 328 // Update the management server with both listener resources. 329 if err := managementServer.Update(ctx, e2e.UpdateOptions{ 330 NodeID: nodeID, 331 Listeners: []*v3listenerpb.Listener{listener1, listener2}, 332 }); err != nil { 333 t.Error(err) 334 } 335 336 // Wait for both listeners to move to "serving" mode. 337 select { 338 case <-ctx.Done(): 339 t.Fatalf("timed out waiting for a mode change update: %v", err) 340 case mode := <-updateCh1: 341 if mode != connectivity.ServingModeServing { 342 t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing) 343 } 344 } 345 select { 346 case <-ctx.Done(): 347 t.Fatalf("timed out waiting for a mode change update: %v", err) 348 case mode := <-updateCh2: 349 if mode != connectivity.ServingModeServing { 350 t.Errorf("listener received new mode %v, want %v", mode, connectivity.ServingModeServing) 351 } 352 } 353 354 // The clientConns created earlier should be able to make RPCs now. 355 waitForSuccessfulRPC(ctx, t, cc1) 356 waitForSuccessfulRPC(ctx, t, cc2) 357 } 358 359 func waitForSuccessfulRPC(ctx context.Context, t *testing.T, cc *grpc.ClientConn) { 360 t.Helper() 361 362 c := testpb.NewTestServiceClient(cc) 363 if _, err := c.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil { 364 t.Fatalf("rpc EmptyCall() failed: %v", err) 365 } 366 } 367 368 func waitForFailedRPC(ctx context.Context, t *testing.T, cc *grpc.ClientConn) { 369 t.Helper() 370 371 // Attempt one RPC before waiting for the ticker to expire. 372 c := testpb.NewTestServiceClient(cc) 373 if _, err := c.EmptyCall(ctx, &testpb.Empty{}); err != nil { 374 return 375 } 376 377 ticker := time.NewTimer(1 * time.Second) 378 defer ticker.Stop() 379 for { 380 select { 381 case <-ctx.Done(): 382 t.Fatalf("failure when waiting for RPCs to fail: %v", ctx.Err()) 383 case <-ticker.C: 384 if _, err := c.EmptyCall(ctx, &testpb.Empty{}); err != nil { 385 return 386 } 387 } 388 } 389 }