github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/server/grpc_server.go (about) 1 // Copyright 2014 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 server 12 13 import ( 14 "strings" 15 "sync/atomic" 16 17 "github.com/cockroachdb/cockroach/pkg/rpc" 18 "github.com/cockroachdb/errors" 19 "google.golang.org/grpc" 20 "google.golang.org/grpc/codes" 21 grpcstatus "google.golang.org/grpc/status" 22 ) 23 24 // grpcServer is a wrapper on top of a grpc.Server that includes an interceptor 25 // and a mode of operation that can instruct the interceptor to refuse certain 26 // RPCs. 27 type grpcServer struct { 28 *grpc.Server 29 mode serveMode 30 } 31 32 func newGRPCServer(rpcCtx *rpc.Context) *grpcServer { 33 s := &grpcServer{} 34 s.mode.set(modeInitializing) 35 s.Server = rpc.NewServerWithInterceptor(rpcCtx, func(path string) error { 36 return s.intercept(path) 37 }) 38 return s 39 } 40 41 type serveMode int32 42 43 // A list of the server states for bootstrap process. 44 const ( 45 // modeInitializing is intended for server initialization process. 46 // It allows only bootstrap, heartbeat and gossip methods 47 // to prevent calls to potentially uninitialized services. 48 modeInitializing serveMode = iota 49 // modeOperational is intended for completely initialized server 50 // and thus allows all RPC methods. 51 modeOperational 52 // modeDraining is intended for an operational server in the process of 53 // shutting down. The difference is that readiness checks will fail. 54 modeDraining 55 ) 56 57 func (s *grpcServer) setMode(mode serveMode) { 58 s.mode.set(mode) 59 } 60 61 func (s *grpcServer) operational() bool { 62 sMode := s.mode.get() 63 return sMode == modeOperational || sMode == modeDraining 64 } 65 66 var rpcsAllowedWhileBootstrapping = map[string]struct{}{ 67 "/cockroach.rpc.Heartbeat/Ping": {}, 68 "/cockroach.gossip.Gossip/Gossip": {}, 69 "/cockroach.server.serverpb.Init/Bootstrap": {}, 70 "/cockroach.server.serverpb.Admin/Health": {}, 71 } 72 73 // intercept implements filtering rules for each server state. 74 func (s *grpcServer) intercept(fullName string) error { 75 if s.operational() { 76 return nil 77 } 78 if _, allowed := rpcsAllowedWhileBootstrapping[fullName]; !allowed { 79 return s.waitingForInitError(fullName) 80 } 81 return nil 82 } 83 84 func (s *serveMode) set(mode serveMode) { 85 atomic.StoreInt32((*int32)(s), int32(mode)) 86 } 87 88 func (s *serveMode) get() serveMode { 89 return serveMode(atomic.LoadInt32((*int32)(s))) 90 } 91 92 // waitingForInitError creates an error indicating that the server cannot run 93 // the specified method until the node has been initialized. 94 func (s *grpcServer) waitingForInitError(methodName string) error { 95 return grpcstatus.Errorf(codes.Unavailable, "node waiting for init; %s not available", methodName) 96 } 97 98 // IsWaitingForInit checks whether the provided error is because the node is 99 // still waiting for initialization. 100 func IsWaitingForInit(err error) bool { 101 s, ok := grpcstatus.FromError(errors.UnwrapAll(err)) 102 return ok && s.Code() == codes.Unavailable && strings.Contains(err.Error(), "node waiting for init") 103 }