Skip to content

Commit c46e4db

Browse files
committed
feat: grpc health service
Implements gRPC Health check using standard grpc library. Reuses the health as reported by gosundheit health checker. Grpc health server implements also streaming Watch method, so it was necessary to add Stop() failback to stop server even if some clients ignore NOT_SERVING status. Timeout for graceful shutdown was chosen to match timeouts for http and telemetry servers. Signed-off-by: Robert Moucha <robert.moucha@gooddata.com>
1 parent 0f7554f commit c46e4db

File tree

1 file changed

+39
-1
lines changed

1 file changed

+39
-1
lines changed

cmd/dex/serve.go

+39-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
"github.com/spf13/cobra"
3333
"google.golang.org/grpc"
3434
"google.golang.org/grpc/credentials"
35+
"google.golang.org/grpc/health"
36+
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
3537
"google.golang.org/grpc/reflection"
3638

3739
"github.com/dexidp/dex/api/v2"
@@ -59,6 +61,8 @@ var buildInfo = prometheus.NewGaugeVec(
5961
[]string{"version", "go_version", "platform"},
6062
)
6163

64+
var healthCheckPeriod = 15 * time.Second
65+
6266
func commandServe() *cobra.Command {
6367
options := serveOptions{}
6468

@@ -379,7 +383,7 @@ func runServe(options serveOptions) error {
379383
CheckName: "storage",
380384
CheckFunc: storage.NewCustomHealthCheckFunc(serverConfig.Storage, serverConfig.Now),
381385
},
382-
gosundheit.ExecutionPeriod(15*time.Second),
386+
gosundheit.ExecutionPeriod(healthCheckPeriod),
383387
gosundheit.InitiallyPassing(true),
384388
)
385389

@@ -508,8 +512,24 @@ func runServe(options serveOptions) error {
508512
}
509513

510514
grpcSrv := grpc.NewServer(grpcOptions...)
515+
healthcheck := health.NewServer()
516+
healthgrpc.RegisterHealthServer(grpcSrv, healthcheck)
511517
api.RegisterDexServer(grpcSrv, server.NewAPI(serverConfig.Storage, logger, version, serv))
512518

519+
go func() {
520+
var status healthgrpc.HealthCheckResponse_ServingStatus
521+
for {
522+
switch healthChecker.IsHealthy() {
523+
case true:
524+
status = healthgrpc.HealthCheckResponse_SERVING
525+
default:
526+
status = healthgrpc.HealthCheckResponse_NOT_SERVING
527+
}
528+
healthcheck.SetServingStatus("", status)
529+
time.Sleep(healthCheckPeriod)
530+
}
531+
}()
532+
513533
grpcMetrics.InitializeMetrics(grpcSrv)
514534
if c.GRPC.Reflection {
515535
logger.Info("enabling reflection in grpc service")
@@ -520,6 +540,24 @@ func runServe(options serveOptions) error {
520540
return grpcSrv.Serve(grpcListener)
521541
}, func(err error) {
522542
logger.Debug("starting graceful shutdown", "server", "grpc")
543+
done := make(chan struct{})
544+
545+
go func() {
546+
healthcheck.Shutdown()
547+
grpcSrv.GracefulStop()
548+
close(done)
549+
}()
550+
551+
select {
552+
case <-done:
553+
// Graceful shutdown completed within the timeout
554+
logger.Debug("Graceful shutdown completed", "server", "grpc")
555+
case <-time.After(time.Minute):
556+
// Timeout reached, force stop the server
557+
logger.Debug("Graceful shutdown timed out. forcing shutdown", "server", "grpc")
558+
grpcSrv.Stop()
559+
}
560+
523561
grpcSrv.GracefulStop()
524562
})
525563
}

0 commit comments

Comments
 (0)