pkg/server/healthz.go (75 lines of code) (raw):
package server
import (
"context"
"fmt"
"net"
"net/http"
"net/url"
"os"
"time"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/health/grpc_health_v1"
"k8s.io/klog/v2"
)
const (
readHeaderTimeout = 5 * time.Second
)
type HealthZ struct {
HealthCheckURL *url.URL
UnixSocketPath string
RPCTimeout time.Duration
}
// Serve creates the http handler for serving health requests
func (h *HealthZ) Serve() {
serveMux := http.NewServeMux()
serveMux.HandleFunc(h.HealthCheckURL.EscapedPath(), h.ServeHTTP)
server := &http.Server{
Addr: h.HealthCheckURL.Host,
ReadHeaderTimeout: readHeaderTimeout,
Handler: serveMux,
}
if err := server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
klog.ErrorS(err, "failed to start health check server")
os.Exit(1)
}
}
func (h *HealthZ) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
klog.V(5).Infof("Started health check")
ctx, cancel := context.WithTimeout(context.Background(), h.RPCTimeout)
defer cancel()
conn, err := h.dialUnixSocket()
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
defer conn.Close()
// create the health check grpc client
client := grpc_health_v1.NewHealthClient(conn)
// check health check response against gRPC endpoint.
err = h.checkRPC(ctx, client)
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
klog.V(5).Infof("Completed health check")
}
// checkRPC initiates a grpc request to validate the socket is responding
// sends a gRPC HealthCheckRequest and checks if the HealthCheckResponse is valid.
func (h *HealthZ) checkRPC(ctx context.Context, client grpc_health_v1.HealthClient) error {
v, err := client.Check(ctx, &grpc_health_v1.HealthCheckRequest{})
if err != nil {
return err
}
if v == nil || v.Status != grpc_health_v1.HealthCheckResponse_SERVING {
return fmt.Errorf("expected health check response serving")
}
return nil
}
func (h *HealthZ) dialUnixSocket() (*grpc.ClientConn, error) {
return grpc.Dial(
h.UnixSocketPath,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(func(ctx context.Context, target string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", target)
}),
)
}