github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/grpcutil/grpc_util_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package grpcutil_test 12 13 import ( 14 "context" 15 "net" 16 "strings" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/testutils" 20 "github.com/cockroachdb/cockroach/pkg/util/grpcutil" 21 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 22 "github.com/cockroachdb/errors" 23 "google.golang.org/grpc" 24 healthpb "google.golang.org/grpc/health/grpc_health_v1" 25 ) 26 27 // Implement the grpc health check interface (just because it's the 28 // simplest predefined RPC service I could find that seems unlikely to 29 // change out from under us) with an implementation that shuts itself 30 // down whenever anything calls it. This lets us distinguish errors 31 // caused by server shutdowns during the request from those when the 32 // server was already down. 33 type healthServer struct { 34 grpcServer *grpc.Server 35 } 36 37 func (hs healthServer) Check( 38 ctx context.Context, req *healthpb.HealthCheckRequest, 39 ) (*healthpb.HealthCheckResponse, error) { 40 hs.grpcServer.Stop() 41 42 // Wait for the shutdown to happen before returning from this 43 // method. 44 <-ctx.Done() 45 return nil, errors.New("no one should see this") 46 } 47 48 func (hs healthServer) Watch(*healthpb.HealthCheckRequest, healthpb.Health_WatchServer) error { 49 panic("not implemented") 50 } 51 52 func TestRequestDidNotStart(t *testing.T) { 53 defer leaktest.AfterTest(t)() 54 55 t.Skip("https://github.com/cockroachdb/cockroach/issues/19708") 56 57 lis, err := net.Listen("tcp", "127.0.0.1:0") 58 if err != nil { 59 t.Fatal(err) 60 } 61 defer func() { 62 _ = lis.Close() 63 }() 64 65 server := grpc.NewServer() 66 hs := healthServer{server} 67 healthpb.RegisterHealthServer(server, hs) 68 go func() { 69 _ = server.Serve(lis) 70 }() 71 72 conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure()) 73 if err != nil { 74 t.Fatal(err) 75 } 76 defer func() { 77 _ = conn.Close() 78 }() 79 client := healthpb.NewHealthClient(conn) 80 81 // The first time, the request will start and we'll get a 82 // "connection is closing" message. 83 _, err = client.Check(context.Background(), &healthpb.HealthCheckRequest{}) 84 if err == nil { 85 t.Fatal("did not get expected error") 86 } else if grpcutil.RequestDidNotStart(err) { 87 t.Fatalf("request should have started, but got %s", err) 88 } else if !strings.Contains(err.Error(), "is closing") { 89 // This assertion is not essential to this test, but since this 90 // logic is sensitive to grpc error handling details it's safer to 91 // make the test fail when anything changes. This error could be 92 // either "transport is closing" or "connection is closing" 93 t.Fatalf("expected 'is closing' error but got %s", err) 94 } 95 96 // Afterwards, the request will fail immediately without being sent. 97 // But if we try too soon, there's a chance the transport hasn't 98 // been put in the "transient failure" state yet and we get a 99 // different error. 100 testutils.SucceedsSoon(t, func() error { 101 _, err = client.Check(context.Background(), &healthpb.HealthCheckRequest{}) 102 if err == nil { 103 return errors.New("did not get expected error") 104 } else if !grpcutil.RequestDidNotStart(err) { 105 return errors.Errorf("request should not have started, but got %s", err) 106 } 107 return nil 108 }) 109 110 // Once the transport is in the "transient failure" state it should 111 // stay that way, and every subsequent request will fail 112 // immediately. 113 _, err = client.Check(context.Background(), &healthpb.HealthCheckRequest{}) 114 if err == nil { 115 t.Fatal("did not get expected error") 116 } else if !grpcutil.RequestDidNotStart(err) { 117 t.Fatalf("request should not have started, but got %s", err) 118 } 119 }