From 1dfde78d17e1583ea8b1d06b26d41cd3dca59ea3 Mon Sep 17 00:00:00 2001 From: Andrew Mason Date: Sun, 7 Mar 2021 20:39:01 -0500 Subject: [PATCH] Add support for additional service healthchecks in grpcserver Then, update vtadmin's API to default to registering itself as SERVING under the "vtadmin.VTAdminServer" name. Signed-off-by: Andrew Mason --- go/vt/vtadmin/api.go | 9 +++++++++ go/vt/vtadmin/grpcserver/server.go | 28 +++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/go/vt/vtadmin/api.go b/go/vt/vtadmin/api.go index d6ad2ab3cee..28ace6158f0 100644 --- a/go/vt/vtadmin/api.go +++ b/go/vt/vtadmin/api.go @@ -58,6 +58,11 @@ type API struct { // NewAPI returns a new API, configured to service the given set of clusters, // and configured with the given gRPC and HTTP server options. +// +// If opts.Services is nil, NewAPI will automatically add +// "vtadmin.VTAdminServer" to the list of services queryable in the healthcheck +// service. Callers can opt-out of this behavior by explicitly setting this +// value to the empty slice. func NewAPI(clusters []*cluster.Cluster, opts grpcserver.Options, httpOpts vtadminhttp.Options) *API { clusterMap := make(map[string]*cluster.Cluster, len(clusters)) for _, cluster := range clusters { @@ -68,6 +73,10 @@ func NewAPI(clusters []*cluster.Cluster, opts grpcserver.Options, httpOpts vtadm return c1.ID < c2.ID }).Sort(clusters) + if opts.Services == nil { + opts.Services = []string{"vtadmin.VTAdminServer"} + } + serv := grpcserver.New("vtadmin", opts) serv.Router().HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok\n")) diff --git a/go/vt/vtadmin/grpcserver/server.go b/go/vt/vtadmin/grpcserver/server.go index 77d23ceb663..ea3e469e150 100644 --- a/go/vt/vtadmin/grpcserver/server.go +++ b/go/vt/vtadmin/grpcserver/server.go @@ -63,8 +63,23 @@ type Options struct { // EnableTracing specifies whether to install opentracing interceptors on // the gRPC server. EnableTracing bool + // Services is a list of service names to declare as SERVING in health + // checks. Names should be fully-qualified (package_name.service_name, e.g. + // vtadmin.VTAdminServer, not VTAdminServer), and must be unique for a + // single Server instance. Users of this package are responsible for + // ensuring they do not pass a list with duplicate service names. + // + // The service name "grpc.health.v1.Health" is reserved by this package in + // order to power the healthcheck service. Attempting to pass this in the + // Services list to a grpcserver will be ignored. + // + // See https://github.com/grpc/grpc/blob/7324556353e831c57d30973db33df489c3ed3576/doc/health-checking.md + // for more details on healthchecking. + Services []string } +const healthServiceName = "grpc.health.v1.Health" // reserved health service name + // Server provides a multiplexed gRPC/HTTP server. type Server struct { name string @@ -201,9 +216,16 @@ func (s *Server) ListenAndServe() error { // nolint:funlen shutdown <- err }() - // (TODO:@amason) Figure out a good abstraction to have other services - // register themselves. - s.healthServer.SetServingStatus("grpc.health.v1.Health", healthpb.HealthCheckResponse_SERVING) + s.healthServer.SetServingStatus(healthServiceName, healthpb.HealthCheckResponse_SERVING) + + for _, name := range s.opts.Services { + if name == healthServiceName { + log.Warningf("Attempted to register a service under the reserved healthcheck service name %s; ignoring", healthServiceName) + continue + } + + s.healthServer.SetServingStatus(name, healthpb.HealthCheckResponse_SERVING) + } s.setServing(true) log.Infof("server %s listening on %s", s.name, s.opts.Addr)