google.golang.org/grpc@v1.62.1/test/healthcheck_test.go (about) 1 /* 2 * 3 * Copyright 2018 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 test 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "net" 26 "sync" 27 "testing" 28 "time" 29 30 "google.golang.org/grpc" 31 "google.golang.org/grpc/codes" 32 "google.golang.org/grpc/connectivity" 33 "google.golang.org/grpc/credentials/insecure" 34 "google.golang.org/grpc/health" 35 "google.golang.org/grpc/internal" 36 "google.golang.org/grpc/internal/channelz" 37 "google.golang.org/grpc/internal/grpctest" 38 "google.golang.org/grpc/internal/testutils" 39 "google.golang.org/grpc/resolver" 40 "google.golang.org/grpc/resolver/manual" 41 "google.golang.org/grpc/status" 42 43 healthgrpc "google.golang.org/grpc/health/grpc_health_v1" 44 healthpb "google.golang.org/grpc/health/grpc_health_v1" 45 testgrpc "google.golang.org/grpc/interop/grpc_testing" 46 testpb "google.golang.org/grpc/interop/grpc_testing" 47 ) 48 49 var testHealthCheckFunc = internal.HealthCheckFunc 50 51 func newTestHealthServer() *testHealthServer { 52 return newTestHealthServerWithWatchFunc(defaultWatchFunc) 53 } 54 55 func newTestHealthServerWithWatchFunc(f healthWatchFunc) *testHealthServer { 56 return &testHealthServer{ 57 watchFunc: f, 58 update: make(chan struct{}, 1), 59 status: make(map[string]healthpb.HealthCheckResponse_ServingStatus), 60 } 61 } 62 63 // defaultWatchFunc will send a HealthCheckResponse to the client whenever SetServingStatus is called. 64 func defaultWatchFunc(s *testHealthServer, in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { 65 if in.Service != "foo" { 66 return status.Error(codes.FailedPrecondition, 67 "the defaultWatchFunc only handles request with service name to be \"foo\"") 68 } 69 var done bool 70 for { 71 select { 72 case <-stream.Context().Done(): 73 done = true 74 case <-s.update: 75 } 76 if done { 77 break 78 } 79 s.mu.Lock() 80 resp := &healthpb.HealthCheckResponse{ 81 Status: s.status[in.Service], 82 } 83 s.mu.Unlock() 84 stream.SendMsg(resp) 85 } 86 return nil 87 } 88 89 type healthWatchFunc func(s *testHealthServer, in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error 90 91 type testHealthServer struct { 92 healthgrpc.UnimplementedHealthServer 93 watchFunc healthWatchFunc 94 mu sync.Mutex 95 status map[string]healthpb.HealthCheckResponse_ServingStatus 96 update chan struct{} 97 } 98 99 func (s *testHealthServer) Check(ctx context.Context, in *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) { 100 return &healthpb.HealthCheckResponse{ 101 Status: healthpb.HealthCheckResponse_SERVING, 102 }, nil 103 } 104 105 func (s *testHealthServer) Watch(in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { 106 return s.watchFunc(s, in, stream) 107 } 108 109 // SetServingStatus is called when need to reset the serving status of a service 110 // or insert a new service entry into the statusMap. 111 func (s *testHealthServer) SetServingStatus(service string, status healthpb.HealthCheckResponse_ServingStatus) { 112 s.mu.Lock() 113 s.status[service] = status 114 select { 115 case <-s.update: 116 default: 117 } 118 s.update <- struct{}{} 119 s.mu.Unlock() 120 } 121 122 func setupHealthCheckWrapper() (hcEnterChan chan struct{}, hcExitChan chan struct{}, wrapper internal.HealthChecker) { 123 hcEnterChan = make(chan struct{}) 124 hcExitChan = make(chan struct{}) 125 wrapper = func(ctx context.Context, newStream func(string) (any, error), update func(connectivity.State, error), service string) error { 126 close(hcEnterChan) 127 defer close(hcExitChan) 128 return testHealthCheckFunc(ctx, newStream, update, service) 129 } 130 return 131 } 132 133 func setupServer(t *testing.T, watchFunc healthWatchFunc) (*grpc.Server, net.Listener, *testHealthServer) { 134 t.Helper() 135 136 lis, err := net.Listen("tcp", "localhost:0") 137 if err != nil { 138 t.Fatalf("net.Listen() failed: %v", err) 139 } 140 141 var ts *testHealthServer 142 if watchFunc != nil { 143 ts = newTestHealthServerWithWatchFunc(watchFunc) 144 } else { 145 ts = newTestHealthServer() 146 } 147 s := grpc.NewServer() 148 healthgrpc.RegisterHealthServer(s, ts) 149 testgrpc.RegisterTestServiceServer(s, &testServer{}) 150 go s.Serve(lis) 151 t.Cleanup(func() { s.Stop() }) 152 return s, lis, ts 153 } 154 155 type clientConfig struct { 156 balancerName string 157 testHealthCheckFuncWrapper internal.HealthChecker 158 extraDialOption []grpc.DialOption 159 } 160 161 func setupClient(t *testing.T, c *clientConfig) (*grpc.ClientConn, *manual.Resolver) { 162 t.Helper() 163 164 r := manual.NewBuilderWithScheme("whatever") 165 opts := []grpc.DialOption{ 166 grpc.WithTransportCredentials(insecure.NewCredentials()), 167 grpc.WithResolvers(r), 168 } 169 if c != nil { 170 if c.balancerName != "" { 171 opts = append(opts, grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, c.balancerName))) 172 } 173 if c.testHealthCheckFuncWrapper != nil { 174 opts = append(opts, internal.WithHealthCheckFunc.(func(internal.HealthChecker) grpc.DialOption)(c.testHealthCheckFuncWrapper)) 175 } 176 opts = append(opts, c.extraDialOption...) 177 } 178 179 cc, err := grpc.Dial(r.Scheme()+":///test.server", opts...) 180 if err != nil { 181 t.Fatalf("grpc.Dial() failed: %v", err) 182 } 183 t.Cleanup(func() { cc.Close() }) 184 return cc, r 185 } 186 187 func (s) TestHealthCheckWatchStateChange(t *testing.T) { 188 _, lis, ts := setupServer(t, nil) 189 190 // The table below shows the expected series of addrConn connectivity transitions when server 191 // updates its health status. As there's only one addrConn corresponds with the ClientConn in this 192 // test, we use ClientConn's connectivity state as the addrConn connectivity state. 193 //+------------------------------+-------------------------------------------+ 194 //| Health Check Returned Status | Expected addrConn Connectivity Transition | 195 //+------------------------------+-------------------------------------------+ 196 //| NOT_SERVING | ->TRANSIENT FAILURE | 197 //| SERVING | ->READY | 198 //| SERVICE_UNKNOWN | ->TRANSIENT FAILURE | 199 //| SERVING | ->READY | 200 //| UNKNOWN | ->TRANSIENT FAILURE | 201 //+------------------------------+-------------------------------------------+ 202 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_NOT_SERVING) 203 204 cc, r := setupClient(t, nil) 205 r.UpdateState(resolver.State{ 206 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 207 ServiceConfig: parseServiceConfig(t, r, `{ 208 "healthCheckConfig": { 209 "serviceName": "foo" 210 }, 211 "loadBalancingConfig": [{"round_robin":{}}] 212 }`)}) 213 214 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 215 defer cancel() 216 testutils.AwaitNotState(ctx, t, cc, connectivity.Idle) 217 testutils.AwaitNotState(ctx, t, cc, connectivity.Connecting) 218 testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure) 219 if s := cc.GetState(); s != connectivity.TransientFailure { 220 t.Fatalf("ClientConn is in %v state, want TRANSIENT FAILURE", s) 221 } 222 223 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVING) 224 testutils.AwaitNotState(ctx, t, cc, connectivity.TransientFailure) 225 if s := cc.GetState(); s != connectivity.Ready { 226 t.Fatalf("ClientConn is in %v state, want READY", s) 227 } 228 229 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVICE_UNKNOWN) 230 testutils.AwaitNotState(ctx, t, cc, connectivity.Ready) 231 if s := cc.GetState(); s != connectivity.TransientFailure { 232 t.Fatalf("ClientConn is in %v state, want TRANSIENT FAILURE", s) 233 } 234 235 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVING) 236 testutils.AwaitNotState(ctx, t, cc, connectivity.TransientFailure) 237 if s := cc.GetState(); s != connectivity.Ready { 238 t.Fatalf("ClientConn is in %v state, want READY", s) 239 } 240 241 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_UNKNOWN) 242 testutils.AwaitNotState(ctx, t, cc, connectivity.Ready) 243 if s := cc.GetState(); s != connectivity.TransientFailure { 244 t.Fatalf("ClientConn is in %v state, want TRANSIENT FAILURE", s) 245 } 246 } 247 248 // If Watch returns Unimplemented, then the ClientConn should go into READY state. 249 func (s) TestHealthCheckHealthServerNotRegistered(t *testing.T) { 250 grpctest.TLogger.ExpectError("Subchannel health check is unimplemented at server side, thus health check is disabled") 251 s := grpc.NewServer() 252 lis, err := net.Listen("tcp", "localhost:0") 253 if err != nil { 254 t.Fatalf("failed to listen due to err: %v", err) 255 } 256 go s.Serve(lis) 257 defer s.Stop() 258 259 cc, r := setupClient(t, nil) 260 r.UpdateState(resolver.State{ 261 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 262 ServiceConfig: parseServiceConfig(t, r, `{ 263 "healthCheckConfig": { 264 "serviceName": "foo" 265 }, 266 "loadBalancingConfig": [{"round_robin":{}}] 267 }`)}) 268 269 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 270 defer cancel() 271 testutils.AwaitNotState(ctx, t, cc, connectivity.Idle) 272 testutils.AwaitNotState(ctx, t, cc, connectivity.Connecting) 273 if s := cc.GetState(); s != connectivity.Ready { 274 t.Fatalf("ClientConn is in %v state, want READY", s) 275 } 276 } 277 278 // In the case of a goaway received, the health check stream should be terminated and health check 279 // function should exit. 280 func (s) TestHealthCheckWithGoAway(t *testing.T) { 281 s, lis, ts := setupServer(t, nil) 282 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVING) 283 284 hcEnterChan, hcExitChan, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 285 cc, r := setupClient(t, &clientConfig{testHealthCheckFuncWrapper: testHealthCheckFuncWrapper}) 286 tc := testgrpc.NewTestServiceClient(cc) 287 r.UpdateState(resolver.State{ 288 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 289 ServiceConfig: parseServiceConfig(t, r, `{ 290 "healthCheckConfig": { 291 "serviceName": "foo" 292 }, 293 "loadBalancingConfig": [{"round_robin":{}}] 294 }`)}) 295 296 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 297 defer cancel() 298 // make some rpcs to make sure connection is working. 299 if err := verifyResultWithDelay(func() (bool, error) { 300 if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != nil { 301 return false, fmt.Errorf("TestService/EmptyCall(_, _) = _, %v, want _, <nil>", err) 302 } 303 return true, nil 304 }); err != nil { 305 t.Fatal(err) 306 } 307 308 // the stream rpc will persist through goaway event. 309 stream, err := tc.FullDuplexCall(ctx, grpc.WaitForReady(true)) 310 if err != nil { 311 t.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err) 312 } 313 respParam := []*testpb.ResponseParameters{{Size: 1}} 314 payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(1)) 315 if err != nil { 316 t.Fatal(err) 317 } 318 req := &testpb.StreamingOutputCallRequest{ 319 ResponseParameters: respParam, 320 Payload: payload, 321 } 322 if err := stream.Send(req); err != nil { 323 t.Fatalf("%v.Send(_) = %v, want <nil>", stream, err) 324 } 325 if _, err := stream.Recv(); err != nil { 326 t.Fatalf("%v.Recv() = _, %v, want _, <nil>", stream, err) 327 } 328 329 select { 330 case <-hcExitChan: 331 t.Fatal("Health check function has exited, which is not expected.") 332 default: 333 } 334 335 // server sends GoAway 336 go s.GracefulStop() 337 338 select { 339 case <-hcExitChan: 340 case <-time.After(5 * time.Second): 341 select { 342 case <-hcEnterChan: 343 default: 344 t.Fatal("Health check function has not entered after 5s.") 345 } 346 t.Fatal("Health check function has not exited after 5s.") 347 } 348 349 // The existing RPC should be still good to proceed. 350 if err := stream.Send(req); err != nil { 351 t.Fatalf("%v.Send(_) = %v, want <nil>", stream, err) 352 } 353 if _, err := stream.Recv(); err != nil { 354 t.Fatalf("%v.Recv() = _, %v, want _, <nil>", stream, err) 355 } 356 } 357 358 func (s) TestHealthCheckWithConnClose(t *testing.T) { 359 s, lis, ts := setupServer(t, nil) 360 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVING) 361 362 hcEnterChan, hcExitChan, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 363 cc, r := setupClient(t, &clientConfig{testHealthCheckFuncWrapper: testHealthCheckFuncWrapper}) 364 tc := testgrpc.NewTestServiceClient(cc) 365 r.UpdateState(resolver.State{ 366 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 367 ServiceConfig: parseServiceConfig(t, r, `{ 368 "healthCheckConfig": { 369 "serviceName": "foo" 370 }, 371 "loadBalancingConfig": [{"round_robin":{}}] 372 }`)}) 373 374 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 375 defer cancel() 376 // make some rpcs to make sure connection is working. 377 if err := verifyResultWithDelay(func() (bool, error) { 378 if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != nil { 379 return false, fmt.Errorf("TestService/EmptyCall(_, _) = _, %v, want _, <nil>", err) 380 } 381 return true, nil 382 }); err != nil { 383 t.Fatal(err) 384 } 385 386 select { 387 case <-hcExitChan: 388 t.Fatal("Health check function has exited, which is not expected.") 389 default: 390 } 391 // server closes the connection 392 s.Stop() 393 394 select { 395 case <-hcExitChan: 396 case <-time.After(5 * time.Second): 397 select { 398 case <-hcEnterChan: 399 default: 400 t.Fatal("Health check function has not entered after 5s.") 401 } 402 t.Fatal("Health check function has not exited after 5s.") 403 } 404 } 405 406 // addrConn drain happens when addrConn gets torn down due to its address being no longer in the 407 // address list returned by the resolver. 408 func (s) TestHealthCheckWithAddrConnDrain(t *testing.T) { 409 _, lis, ts := setupServer(t, nil) 410 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVING) 411 412 hcEnterChan, hcExitChan, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 413 cc, r := setupClient(t, &clientConfig{testHealthCheckFuncWrapper: testHealthCheckFuncWrapper}) 414 tc := testgrpc.NewTestServiceClient(cc) 415 sc := parseServiceConfig(t, r, `{ 416 "healthCheckConfig": { 417 "serviceName": "foo" 418 }, 419 "loadBalancingConfig": [{"round_robin":{}}] 420 }`) 421 r.UpdateState(resolver.State{ 422 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 423 ServiceConfig: sc, 424 }) 425 426 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 427 defer cancel() 428 // make some rpcs to make sure connection is working. 429 if err := verifyResultWithDelay(func() (bool, error) { 430 if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != nil { 431 return false, fmt.Errorf("TestService/EmptyCall(_, _) = _, %v, want _, <nil>", err) 432 } 433 return true, nil 434 }); err != nil { 435 t.Fatal(err) 436 } 437 438 // the stream rpc will persist through goaway event. 439 stream, err := tc.FullDuplexCall(ctx, grpc.WaitForReady(true)) 440 if err != nil { 441 t.Fatalf("%v.FullDuplexCall(_) = _, %v, want <nil>", tc, err) 442 } 443 respParam := []*testpb.ResponseParameters{{Size: 1}} 444 payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(1)) 445 if err != nil { 446 t.Fatal(err) 447 } 448 req := &testpb.StreamingOutputCallRequest{ 449 ResponseParameters: respParam, 450 Payload: payload, 451 } 452 if err := stream.Send(req); err != nil { 453 t.Fatalf("%v.Send(_) = %v, want <nil>", stream, err) 454 } 455 if _, err := stream.Recv(); err != nil { 456 t.Fatalf("%v.Recv() = _, %v, want _, <nil>", stream, err) 457 } 458 459 select { 460 case <-hcExitChan: 461 t.Fatal("Health check function has exited, which is not expected.") 462 default: 463 } 464 // trigger teardown of the ac 465 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "fake address"}}, ServiceConfig: sc}) 466 467 select { 468 case <-hcExitChan: 469 case <-time.After(5 * time.Second): 470 select { 471 case <-hcEnterChan: 472 default: 473 t.Fatal("Health check function has not entered after 5s.") 474 } 475 t.Fatal("Health check function has not exited after 5s.") 476 } 477 478 // The existing RPC should be still good to proceed. 479 if err := stream.Send(req); err != nil { 480 t.Fatalf("%v.Send(_) = %v, want <nil>", stream, err) 481 } 482 if _, err := stream.Recv(); err != nil { 483 t.Fatalf("%v.Recv() = _, %v, want _, <nil>", stream, err) 484 } 485 } 486 487 // ClientConn close will lead to its addrConns being torn down. 488 func (s) TestHealthCheckWithClientConnClose(t *testing.T) { 489 _, lis, ts := setupServer(t, nil) 490 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVING) 491 492 hcEnterChan, hcExitChan, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 493 cc, r := setupClient(t, &clientConfig{testHealthCheckFuncWrapper: testHealthCheckFuncWrapper}) 494 tc := testgrpc.NewTestServiceClient(cc) 495 r.UpdateState(resolver.State{ 496 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 497 ServiceConfig: parseServiceConfig(t, r, `{ 498 "healthCheckConfig": { 499 "serviceName": "foo" 500 }, 501 "loadBalancingConfig": [{"round_robin":{}}] 502 }`)}) 503 504 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 505 defer cancel() 506 // make some rpcs to make sure connection is working. 507 if err := verifyResultWithDelay(func() (bool, error) { 508 if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != nil { 509 return false, fmt.Errorf("TestService/EmptyCall(_, _) = _, %v, want _, <nil>", err) 510 } 511 return true, nil 512 }); err != nil { 513 t.Fatal(err) 514 } 515 516 select { 517 case <-hcExitChan: 518 t.Fatal("Health check function has exited, which is not expected.") 519 default: 520 } 521 522 // trigger addrConn teardown 523 cc.Close() 524 525 select { 526 case <-hcExitChan: 527 case <-time.After(5 * time.Second): 528 select { 529 case <-hcEnterChan: 530 default: 531 t.Fatal("Health check function has not entered after 5s.") 532 } 533 t.Fatal("Health check function has not exited after 5s.") 534 } 535 } 536 537 // This test is to test the logic in the createTransport after the health check function returns which 538 // closes the skipReset channel(since it has not been closed inside health check func) to unblock 539 // onGoAway/onClose goroutine. 540 func (s) TestHealthCheckWithoutSetConnectivityStateCalledAddrConnShutDown(t *testing.T) { 541 watchFunc := func(s *testHealthServer, in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { 542 if in.Service != "delay" { 543 return status.Error(codes.FailedPrecondition, 544 "this special Watch function only handles request with service name to be \"delay\"") 545 } 546 // Do nothing to mock a delay of health check response from server side. 547 // This case is to help with the test that covers the condition that setConnectivityState is not 548 // called inside HealthCheckFunc before the func returns. 549 select { 550 case <-stream.Context().Done(): 551 case <-time.After(5 * time.Second): 552 } 553 return nil 554 } 555 _, lis, ts := setupServer(t, watchFunc) 556 ts.SetServingStatus("delay", healthpb.HealthCheckResponse_SERVING) 557 558 hcEnterChan, hcExitChan, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 559 _, r := setupClient(t, &clientConfig{testHealthCheckFuncWrapper: testHealthCheckFuncWrapper}) 560 561 // The serviceName "delay" is specially handled at server side, where response will not be sent 562 // back to client immediately upon receiving the request (client should receive no response until 563 // test ends). 564 sc := parseServiceConfig(t, r, `{ 565 "healthCheckConfig": { 566 "serviceName": "delay" 567 }, 568 "loadBalancingConfig": [{"round_robin":{}}] 569 }`) 570 r.UpdateState(resolver.State{ 571 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 572 ServiceConfig: sc, 573 }) 574 575 select { 576 case <-hcExitChan: 577 t.Fatal("Health check function has exited, which is not expected.") 578 default: 579 } 580 581 select { 582 case <-hcEnterChan: 583 case <-time.After(5 * time.Second): 584 t.Fatal("Health check function has not been invoked after 5s.") 585 } 586 // trigger teardown of the ac, ac in SHUTDOWN state 587 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: "fake address"}}, ServiceConfig: sc}) 588 589 // The health check func should exit without calling the setConnectivityState func, as server hasn't sent 590 // any response. 591 select { 592 case <-hcExitChan: 593 case <-time.After(5 * time.Second): 594 t.Fatal("Health check function has not exited after 5s.") 595 } 596 // The deferred leakcheck will check whether there's leaked goroutine, which is an indication 597 // whether we closes the skipReset channel to unblock onGoAway/onClose goroutine. 598 } 599 600 // This test is to test the logic in the createTransport after the health check function returns which 601 // closes the allowedToReset channel(since it has not been closed inside health check func) to unblock 602 // onGoAway/onClose goroutine. 603 func (s) TestHealthCheckWithoutSetConnectivityStateCalled(t *testing.T) { 604 watchFunc := func(s *testHealthServer, in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { 605 if in.Service != "delay" { 606 return status.Error(codes.FailedPrecondition, 607 "this special Watch function only handles request with service name to be \"delay\"") 608 } 609 // Do nothing to mock a delay of health check response from server side. 610 // This case is to help with the test that covers the condition that setConnectivityState is not 611 // called inside HealthCheckFunc before the func returns. 612 select { 613 case <-stream.Context().Done(): 614 case <-time.After(5 * time.Second): 615 } 616 return nil 617 } 618 s, lis, ts := setupServer(t, watchFunc) 619 ts.SetServingStatus("delay", healthpb.HealthCheckResponse_SERVING) 620 621 hcEnterChan, hcExitChan, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 622 _, r := setupClient(t, &clientConfig{testHealthCheckFuncWrapper: testHealthCheckFuncWrapper}) 623 624 // The serviceName "delay" is specially handled at server side, where response will not be sent 625 // back to client immediately upon receiving the request (client should receive no response until 626 // test ends). 627 r.UpdateState(resolver.State{ 628 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 629 ServiceConfig: parseServiceConfig(t, r, `{ 630 "healthCheckConfig": { 631 "serviceName": "delay" 632 }, 633 "loadBalancingConfig": [{"round_robin":{}}] 634 }`)}) 635 636 select { 637 case <-hcExitChan: 638 t.Fatal("Health check function has exited, which is not expected.") 639 default: 640 } 641 642 select { 643 case <-hcEnterChan: 644 case <-time.After(5 * time.Second): 645 t.Fatal("Health check function has not been invoked after 5s.") 646 } 647 // trigger transport being closed 648 s.Stop() 649 650 // The health check func should exit without calling the setConnectivityState func, as server hasn't sent 651 // any response. 652 select { 653 case <-hcExitChan: 654 case <-time.After(5 * time.Second): 655 t.Fatal("Health check function has not exited after 5s.") 656 } 657 // The deferred leakcheck will check whether there's leaked goroutine, which is an indication 658 // whether we closes the allowedToReset channel to unblock onGoAway/onClose goroutine. 659 } 660 661 func testHealthCheckDisableWithDialOption(t *testing.T, addr string) { 662 hcEnterChan, _, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 663 cc, r := setupClient(t, &clientConfig{ 664 testHealthCheckFuncWrapper: testHealthCheckFuncWrapper, 665 extraDialOption: []grpc.DialOption{grpc.WithDisableHealthCheck()}, 666 }) 667 tc := testgrpc.NewTestServiceClient(cc) 668 r.UpdateState(resolver.State{ 669 Addresses: []resolver.Address{{Addr: addr}}, 670 ServiceConfig: parseServiceConfig(t, r, `{ 671 "healthCheckConfig": { 672 "serviceName": "foo" 673 }, 674 "loadBalancingConfig": [{"round_robin":{}}] 675 }`)}) 676 677 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 678 defer cancel() 679 // send some rpcs to make sure transport has been created and is ready for use. 680 if err := verifyResultWithDelay(func() (bool, error) { 681 if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != nil { 682 return false, fmt.Errorf("TestService/EmptyCall(_, _) = _, %v, want _, <nil>", err) 683 } 684 return true, nil 685 }); err != nil { 686 t.Fatal(err) 687 } 688 689 select { 690 case <-hcEnterChan: 691 t.Fatal("Health check function has exited, which is not expected.") 692 default: 693 } 694 } 695 696 func testHealthCheckDisableWithBalancer(t *testing.T, addr string) { 697 hcEnterChan, _, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 698 cc, r := setupClient(t, &clientConfig{ 699 testHealthCheckFuncWrapper: testHealthCheckFuncWrapper, 700 }) 701 tc := testgrpc.NewTestServiceClient(cc) 702 r.UpdateState(resolver.State{ 703 Addresses: []resolver.Address{{Addr: addr}}, 704 ServiceConfig: parseServiceConfig(t, r, `{ 705 "healthCheckConfig": { 706 "serviceName": "foo" 707 }, 708 "loadBalancingConfig": [{"pick_first":{}}] 709 }`)}) 710 711 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 712 defer cancel() 713 // send some rpcs to make sure transport has been created and is ready for use. 714 if err := verifyResultWithDelay(func() (bool, error) { 715 if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != nil { 716 return false, fmt.Errorf("TestService/EmptyCall(_, _) = _, %v, want _, <nil>", err) 717 } 718 return true, nil 719 }); err != nil { 720 t.Fatal(err) 721 } 722 723 select { 724 case <-hcEnterChan: 725 t.Fatal("Health check function has started, which is not expected.") 726 default: 727 } 728 } 729 730 func testHealthCheckDisableWithServiceConfig(t *testing.T, addr string) { 731 hcEnterChan, _, testHealthCheckFuncWrapper := setupHealthCheckWrapper() 732 cc, r := setupClient(t, &clientConfig{testHealthCheckFuncWrapper: testHealthCheckFuncWrapper}) 733 tc := testgrpc.NewTestServiceClient(cc) 734 r.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: addr}}}) 735 736 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 737 defer cancel() 738 // send some rpcs to make sure transport has been created and is ready for use. 739 if err := verifyResultWithDelay(func() (bool, error) { 740 if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != nil { 741 return false, fmt.Errorf("TestService/EmptyCall(_, _) = _, %v, want _, <nil>", err) 742 } 743 return true, nil 744 }); err != nil { 745 t.Fatal(err) 746 } 747 748 select { 749 case <-hcEnterChan: 750 t.Fatal("Health check function has started, which is not expected.") 751 default: 752 } 753 } 754 755 func (s) TestHealthCheckDisable(t *testing.T) { 756 _, lis, ts := setupServer(t, nil) 757 ts.SetServingStatus("foo", healthpb.HealthCheckResponse_SERVING) 758 759 // test client side disabling configuration. 760 testHealthCheckDisableWithDialOption(t, lis.Addr().String()) 761 testHealthCheckDisableWithBalancer(t, lis.Addr().String()) 762 testHealthCheckDisableWithServiceConfig(t, lis.Addr().String()) 763 } 764 765 func (s) TestHealthCheckChannelzCountingCallSuccess(t *testing.T) { 766 watchFunc := func(s *testHealthServer, in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { 767 if in.Service != "channelzSuccess" { 768 return status.Error(codes.FailedPrecondition, 769 "this special Watch function only handles request with service name to be \"channelzSuccess\"") 770 } 771 return status.Error(codes.OK, "fake success") 772 } 773 _, lis, _ := setupServer(t, watchFunc) 774 775 _, r := setupClient(t, nil) 776 r.UpdateState(resolver.State{ 777 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 778 ServiceConfig: parseServiceConfig(t, r, `{ 779 "healthCheckConfig": { 780 "serviceName": "channelzSuccess" 781 }, 782 "loadBalancingConfig": [{"round_robin":{}}] 783 }`)}) 784 785 if err := verifyResultWithDelay(func() (bool, error) { 786 cm, _ := channelz.GetTopChannels(0, 0) 787 if len(cm) == 0 { 788 return false, errors.New("channelz.GetTopChannels return 0 top channel") 789 } 790 if len(cm[0].SubChans) == 0 { 791 return false, errors.New("there is 0 subchannel") 792 } 793 var id int64 794 for k := range cm[0].SubChans { 795 id = k 796 break 797 } 798 scm := channelz.GetSubChannel(id) 799 if scm == nil || scm.ChannelData == nil { 800 return false, errors.New("nil subchannel metric or nil subchannel metric ChannelData returned") 801 } 802 // exponential backoff retry may result in more than one health check call. 803 if scm.ChannelData.CallsStarted > 0 && scm.ChannelData.CallsSucceeded > 0 && scm.ChannelData.CallsFailed == 0 { 804 return true, nil 805 } 806 return false, fmt.Errorf("got %d CallsStarted, %d CallsSucceeded, want >0 >0", scm.ChannelData.CallsStarted, scm.ChannelData.CallsSucceeded) 807 }); err != nil { 808 t.Fatal(err) 809 } 810 } 811 812 func (s) TestHealthCheckChannelzCountingCallFailure(t *testing.T) { 813 watchFunc := func(s *testHealthServer, in *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error { 814 if in.Service != "channelzFailure" { 815 return status.Error(codes.FailedPrecondition, 816 "this special Watch function only handles request with service name to be \"channelzFailure\"") 817 } 818 return status.Error(codes.Internal, "fake failure") 819 } 820 _, lis, _ := setupServer(t, watchFunc) 821 822 _, r := setupClient(t, nil) 823 r.UpdateState(resolver.State{ 824 Addresses: []resolver.Address{{Addr: lis.Addr().String()}}, 825 ServiceConfig: parseServiceConfig(t, r, `{ 826 "healthCheckConfig": { 827 "serviceName": "channelzFailure" 828 }, 829 "loadBalancingConfig": [{"round_robin":{}}] 830 }`)}) 831 832 if err := verifyResultWithDelay(func() (bool, error) { 833 cm, _ := channelz.GetTopChannels(0, 0) 834 if len(cm) == 0 { 835 return false, errors.New("channelz.GetTopChannels return 0 top channel") 836 } 837 if len(cm[0].SubChans) == 0 { 838 return false, errors.New("there is 0 subchannel") 839 } 840 var id int64 841 for k := range cm[0].SubChans { 842 id = k 843 break 844 } 845 scm := channelz.GetSubChannel(id) 846 if scm == nil || scm.ChannelData == nil { 847 return false, errors.New("nil subchannel metric or nil subchannel metric ChannelData returned") 848 } 849 // exponential backoff retry may result in more than one health check call. 850 if scm.ChannelData.CallsStarted > 0 && scm.ChannelData.CallsFailed > 0 && scm.ChannelData.CallsSucceeded == 0 { 851 return true, nil 852 } 853 return false, fmt.Errorf("got %d CallsStarted, %d CallsFailed, want >0, >0", scm.ChannelData.CallsStarted, scm.ChannelData.CallsFailed) 854 }); err != nil { 855 t.Fatal(err) 856 } 857 } 858 859 // healthCheck is a helper function to make a unary health check RPC and return 860 // the response. 861 func healthCheck(d time.Duration, cc *grpc.ClientConn, service string) (*healthpb.HealthCheckResponse, error) { 862 ctx, cancel := context.WithTimeout(context.Background(), d) 863 defer cancel() 864 hc := healthgrpc.NewHealthClient(cc) 865 return hc.Check(ctx, &healthpb.HealthCheckRequest{Service: service}) 866 } 867 868 // verifyHealthCheckStatus is a helper function to verify that the current 869 // health status of the service matches the one passed in 'wantStatus'. 870 func verifyHealthCheckStatus(t *testing.T, d time.Duration, cc *grpc.ClientConn, service string, wantStatus healthpb.HealthCheckResponse_ServingStatus) { 871 t.Helper() 872 resp, err := healthCheck(d, cc, service) 873 if err != nil { 874 t.Fatalf("Health/Check(_, _) = _, %v, want _, <nil>", err) 875 } 876 if resp.Status != wantStatus { 877 t.Fatalf("Got the serving status %v, want %v", resp.Status, wantStatus) 878 } 879 } 880 881 // verifyHealthCheckErrCode is a helper function to verify that a unary health 882 // check RPC returns an error with a code set to 'wantCode'. 883 func verifyHealthCheckErrCode(t *testing.T, d time.Duration, cc *grpc.ClientConn, service string, wantCode codes.Code) { 884 t.Helper() 885 if _, err := healthCheck(d, cc, service); status.Code(err) != wantCode { 886 t.Fatalf("Health/Check() got errCode %v, want %v", status.Code(err), wantCode) 887 } 888 } 889 890 // newHealthCheckStream is a helper function to start a health check streaming 891 // RPC, and returns the stream. 892 func newHealthCheckStream(t *testing.T, cc *grpc.ClientConn, service string) (healthgrpc.Health_WatchClient, context.CancelFunc) { 893 t.Helper() 894 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 895 hc := healthgrpc.NewHealthClient(cc) 896 stream, err := hc.Watch(ctx, &healthpb.HealthCheckRequest{Service: service}) 897 if err != nil { 898 t.Fatalf("hc.Watch(_, %v) failed: %v", service, err) 899 } 900 return stream, cancel 901 } 902 903 // healthWatchChecker is a helper function to verify that the next health 904 // status returned on the given stream matches the one passed in 'wantStatus'. 905 func healthWatchChecker(t *testing.T, stream healthgrpc.Health_WatchClient, wantStatus healthpb.HealthCheckResponse_ServingStatus) { 906 t.Helper() 907 response, err := stream.Recv() 908 if err != nil { 909 t.Fatalf("stream.Recv() failed: %v", err) 910 } 911 if response.Status != wantStatus { 912 t.Fatalf("got servingStatus %v, want %v", response.Status, wantStatus) 913 } 914 } 915 916 // TestHealthCheckSuccess invokes the unary Check() RPC on the health server in 917 // a successful case. 918 func (s) TestHealthCheckSuccess(t *testing.T) { 919 for _, e := range listTestEnv() { 920 testHealthCheckSuccess(t, e) 921 } 922 } 923 924 func testHealthCheckSuccess(t *testing.T, e env) { 925 te := newTest(t, e) 926 te.enableHealthServer = true 927 te.startServer(&testServer{security: e.security}) 928 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 929 defer te.tearDown() 930 931 verifyHealthCheckErrCode(t, 1*time.Second, te.clientConn(), defaultHealthService, codes.OK) 932 } 933 934 // TestHealthCheckFailure invokes the unary Check() RPC on the health server 935 // with an expired context and expects the RPC to fail. 936 func (s) TestHealthCheckFailure(t *testing.T) { 937 for _, e := range listTestEnv() { 938 testHealthCheckFailure(t, e) 939 } 940 } 941 942 func testHealthCheckFailure(t *testing.T, e env) { 943 te := newTest(t, e) 944 te.declareLogNoise( 945 "Failed to dial ", 946 "grpc: the client connection is closing; please retry", 947 ) 948 te.enableHealthServer = true 949 te.startServer(&testServer{security: e.security}) 950 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 951 defer te.tearDown() 952 953 verifyHealthCheckErrCode(t, 0*time.Second, te.clientConn(), defaultHealthService, codes.DeadlineExceeded) 954 awaitNewConnLogOutput() 955 } 956 957 // TestHealthCheckOff makes a unary Check() RPC on the health server where the 958 // health status of the defaultHealthService is not set, and therefore expects 959 // an error code 'codes.NotFound'. 960 func (s) TestHealthCheckOff(t *testing.T) { 961 for _, e := range listTestEnv() { 962 // TODO(bradfitz): Temporarily skip this env due to #619. 963 if e.name == "handler-tls" { 964 continue 965 } 966 testHealthCheckOff(t, e) 967 } 968 } 969 970 func testHealthCheckOff(t *testing.T, e env) { 971 te := newTest(t, e) 972 te.enableHealthServer = true 973 te.startServer(&testServer{security: e.security}) 974 defer te.tearDown() 975 976 verifyHealthCheckErrCode(t, 1*time.Second, te.clientConn(), defaultHealthService, codes.NotFound) 977 } 978 979 // TestHealthWatchMultipleClients makes a streaming Watch() RPC on the health 980 // server with multiple clients and expects the same status on both streams. 981 func (s) TestHealthWatchMultipleClients(t *testing.T) { 982 for _, e := range listTestEnv() { 983 testHealthWatchMultipleClients(t, e) 984 } 985 } 986 987 func testHealthWatchMultipleClients(t *testing.T, e env) { 988 te := newTest(t, e) 989 te.enableHealthServer = true 990 te.startServer(&testServer{security: e.security}) 991 defer te.tearDown() 992 993 cc := te.clientConn() 994 stream1, cf1 := newHealthCheckStream(t, cc, defaultHealthService) 995 defer cf1() 996 healthWatchChecker(t, stream1, healthpb.HealthCheckResponse_SERVICE_UNKNOWN) 997 998 stream2, cf2 := newHealthCheckStream(t, cc, defaultHealthService) 999 defer cf2() 1000 healthWatchChecker(t, stream2, healthpb.HealthCheckResponse_SERVICE_UNKNOWN) 1001 1002 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_NOT_SERVING) 1003 healthWatchChecker(t, stream1, healthpb.HealthCheckResponse_NOT_SERVING) 1004 healthWatchChecker(t, stream2, healthpb.HealthCheckResponse_NOT_SERVING) 1005 } 1006 1007 // TestHealthWatchSameStatusmakes a streaming Watch() RPC on the health server 1008 // and makes sure that the health status of the server is as expected after 1009 // multiple calls to SetServingStatus with the same status. 1010 func (s) TestHealthWatchSameStatus(t *testing.T) { 1011 for _, e := range listTestEnv() { 1012 testHealthWatchSameStatus(t, e) 1013 } 1014 } 1015 1016 func testHealthWatchSameStatus(t *testing.T, e env) { 1017 te := newTest(t, e) 1018 te.enableHealthServer = true 1019 te.startServer(&testServer{security: e.security}) 1020 defer te.tearDown() 1021 1022 stream, cf := newHealthCheckStream(t, te.clientConn(), defaultHealthService) 1023 defer cf() 1024 1025 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_SERVICE_UNKNOWN) 1026 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 1027 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_SERVING) 1028 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 1029 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_NOT_SERVING) 1030 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_NOT_SERVING) 1031 } 1032 1033 // TestHealthWatchServiceStatusSetBeforeStartingServer starts a health server 1034 // on which the health status for the defaultService is set before the gRPC 1035 // server is started, and expects the correct health status to be returned. 1036 func (s) TestHealthWatchServiceStatusSetBeforeStartingServer(t *testing.T) { 1037 for _, e := range listTestEnv() { 1038 testHealthWatchSetServiceStatusBeforeStartingServer(t, e) 1039 } 1040 } 1041 1042 func testHealthWatchSetServiceStatusBeforeStartingServer(t *testing.T, e env) { 1043 hs := health.NewServer() 1044 te := newTest(t, e) 1045 te.healthServer = hs 1046 hs.SetServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 1047 te.startServer(&testServer{security: e.security}) 1048 defer te.tearDown() 1049 1050 stream, cf := newHealthCheckStream(t, te.clientConn(), defaultHealthService) 1051 defer cf() 1052 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_SERVING) 1053 } 1054 1055 // TestHealthWatchDefaultStatusChange verifies the simple case where the 1056 // service starts off with a SERVICE_UNKNOWN status (because SetServingStatus 1057 // hasn't been called yet) and then moves to SERVING after SetServingStatus is 1058 // called. 1059 func (s) TestHealthWatchDefaultStatusChange(t *testing.T) { 1060 for _, e := range listTestEnv() { 1061 testHealthWatchDefaultStatusChange(t, e) 1062 } 1063 } 1064 1065 func testHealthWatchDefaultStatusChange(t *testing.T, e env) { 1066 te := newTest(t, e) 1067 te.enableHealthServer = true 1068 te.startServer(&testServer{security: e.security}) 1069 defer te.tearDown() 1070 1071 stream, cf := newHealthCheckStream(t, te.clientConn(), defaultHealthService) 1072 defer cf() 1073 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_SERVICE_UNKNOWN) 1074 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 1075 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_SERVING) 1076 } 1077 1078 // TestHealthWatchSetServiceStatusBeforeClientCallsWatch verifies the case 1079 // where the health status is set to SERVING before the client calls Watch(). 1080 func (s) TestHealthWatchSetServiceStatusBeforeClientCallsWatch(t *testing.T) { 1081 for _, e := range listTestEnv() { 1082 testHealthWatchSetServiceStatusBeforeClientCallsWatch(t, e) 1083 } 1084 } 1085 1086 func testHealthWatchSetServiceStatusBeforeClientCallsWatch(t *testing.T, e env) { 1087 te := newTest(t, e) 1088 te.enableHealthServer = true 1089 te.startServer(&testServer{security: e.security}) 1090 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 1091 defer te.tearDown() 1092 1093 stream, cf := newHealthCheckStream(t, te.clientConn(), defaultHealthService) 1094 defer cf() 1095 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_SERVING) 1096 } 1097 1098 // TestHealthWatchOverallServerHealthChange verifies setting the overall status 1099 // of the server by using the empty service name. 1100 func (s) TestHealthWatchOverallServerHealthChange(t *testing.T) { 1101 for _, e := range listTestEnv() { 1102 testHealthWatchOverallServerHealthChange(t, e) 1103 } 1104 } 1105 1106 func testHealthWatchOverallServerHealthChange(t *testing.T, e env) { 1107 te := newTest(t, e) 1108 te.enableHealthServer = true 1109 te.startServer(&testServer{security: e.security}) 1110 defer te.tearDown() 1111 1112 stream, cf := newHealthCheckStream(t, te.clientConn(), "") 1113 defer cf() 1114 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_SERVING) 1115 te.setHealthServingStatus("", healthpb.HealthCheckResponse_NOT_SERVING) 1116 healthWatchChecker(t, stream, healthpb.HealthCheckResponse_NOT_SERVING) 1117 } 1118 1119 // TestUnknownHandler verifies that an expected error is returned (by setting 1120 // the unknownHandler on the server) for a service which is not exposed to the 1121 // client. 1122 func (s) TestUnknownHandler(t *testing.T) { 1123 // An example unknownHandler that returns a different code and a different 1124 // method, making sure that we do not expose what methods are implemented to 1125 // a client that is not authenticated. 1126 unknownHandler := func(srv any, stream grpc.ServerStream) error { 1127 return status.Error(codes.Unauthenticated, "user unauthenticated") 1128 } 1129 for _, e := range listTestEnv() { 1130 // TODO(bradfitz): Temporarily skip this env due to #619. 1131 if e.name == "handler-tls" { 1132 continue 1133 } 1134 testUnknownHandler(t, e, unknownHandler) 1135 } 1136 } 1137 1138 func testUnknownHandler(t *testing.T, e env, unknownHandler grpc.StreamHandler) { 1139 te := newTest(t, e) 1140 te.unknownHandler = unknownHandler 1141 te.startServer(&testServer{security: e.security}) 1142 defer te.tearDown() 1143 verifyHealthCheckErrCode(t, 1*time.Second, te.clientConn(), "", codes.Unauthenticated) 1144 } 1145 1146 // TestHealthCheckServingStatus makes a streaming Watch() RPC on the health 1147 // server and verifies a bunch of health status transitions. 1148 func (s) TestHealthCheckServingStatus(t *testing.T) { 1149 for _, e := range listTestEnv() { 1150 testHealthCheckServingStatus(t, e) 1151 } 1152 } 1153 1154 func testHealthCheckServingStatus(t *testing.T, e env) { 1155 te := newTest(t, e) 1156 te.enableHealthServer = true 1157 te.startServer(&testServer{security: e.security}) 1158 defer te.tearDown() 1159 1160 cc := te.clientConn() 1161 verifyHealthCheckStatus(t, 1*time.Second, cc, "", healthpb.HealthCheckResponse_SERVING) 1162 verifyHealthCheckErrCode(t, 1*time.Second, cc, defaultHealthService, codes.NotFound) 1163 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_SERVING) 1164 verifyHealthCheckStatus(t, 1*time.Second, cc, defaultHealthService, healthpb.HealthCheckResponse_SERVING) 1165 te.setHealthServingStatus(defaultHealthService, healthpb.HealthCheckResponse_NOT_SERVING) 1166 verifyHealthCheckStatus(t, 1*time.Second, cc, defaultHealthService, healthpb.HealthCheckResponse_NOT_SERVING) 1167 }