2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2017-12-27 04:35:22 +00:00
|
|
|
package checks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
2018-01-25 22:26:15 +00:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2017-12-27 04:35:22 +00:00
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
hv1 "google.golang.org/grpc/health/grpc_health_v1"
|
2020-05-20 20:26:26 +00:00
|
|
|
"google.golang.org/grpc/resolver"
|
2017-12-27 04:35:22 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// GrpcHealthProbe connects to gRPC application and queries health service for application/service status.
|
|
|
|
type GrpcHealthProbe struct {
|
2018-01-25 22:26:15 +00:00
|
|
|
server string
|
|
|
|
request *hv1.HealthCheckRequest
|
|
|
|
timeout time.Duration
|
|
|
|
dialOptions []grpc.DialOption
|
2017-12-27 04:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewGrpcHealthProbe constructs GrpcHealthProbe from target string in format
|
|
|
|
// server[/service]
|
|
|
|
// If service is omitted, health of the entire application is probed
|
|
|
|
func NewGrpcHealthProbe(target string, timeout time.Duration, tlsConfig *tls.Config) *GrpcHealthProbe {
|
|
|
|
serverAndService := strings.SplitN(target, "/", 2)
|
|
|
|
|
|
|
|
request := hv1.HealthCheckRequest{}
|
|
|
|
if len(serverAndService) > 1 {
|
|
|
|
request.Service = serverAndService[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
var dialOptions = []grpc.DialOption{}
|
|
|
|
|
|
|
|
if tlsConfig != nil {
|
|
|
|
dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
|
|
|
|
} else {
|
2022-11-07 16:34:30 +00:00
|
|
|
//nolint:staticcheck
|
2017-12-27 04:35:22 +00:00
|
|
|
dialOptions = append(dialOptions, grpc.WithInsecure())
|
|
|
|
}
|
|
|
|
|
|
|
|
return &GrpcHealthProbe{
|
|
|
|
request: &request,
|
|
|
|
timeout: timeout,
|
|
|
|
dialOptions: dialOptions,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the target of this GrpcHealthProbe is healthy
|
|
|
|
// If nil is returned, target is healthy, otherwise target is not healthy
|
2019-09-26 02:55:52 +00:00
|
|
|
func (probe *GrpcHealthProbe) Check(target string) error {
|
|
|
|
serverAndService := strings.SplitN(target, "/", 2)
|
2020-05-20 20:26:26 +00:00
|
|
|
serverWithScheme := fmt.Sprintf("%s:///%s", resolver.GetDefaultScheme(), serverAndService[0])
|
2019-09-26 02:55:52 +00:00
|
|
|
|
2017-12-27 04:35:22 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), probe.timeout)
|
|
|
|
defer cancel()
|
|
|
|
|
2020-05-20 20:26:26 +00:00
|
|
|
connection, err := grpc.DialContext(ctx, serverWithScheme, probe.dialOptions...)
|
2017-12-27 04:35:22 +00:00
|
|
|
if err != nil {
|
2018-01-25 22:29:50 +00:00
|
|
|
return err
|
2017-12-27 04:35:22 +00:00
|
|
|
}
|
|
|
|
defer connection.Close()
|
|
|
|
|
|
|
|
client := hv1.NewHealthClient(connection)
|
|
|
|
response, err := client.Check(ctx, probe.request)
|
|
|
|
if err != nil {
|
2018-01-25 22:29:50 +00:00
|
|
|
return err
|
2017-12-27 04:35:22 +00:00
|
|
|
}
|
2020-09-22 18:15:05 +00:00
|
|
|
if response.Status != hv1.HealthCheckResponse_SERVING {
|
|
|
|
return fmt.Errorf("gRPC %s serving status: %s", target, response.Status)
|
2017-12-27 04:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|