From ab579128b94a10dc3603a579d8b8ffba78153e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ask=20Bj=C3=B8rn=20Hansen?= Date: Sun, 2 Jul 2023 23:49:05 -0700 Subject: [PATCH] health: basic health checker from the monitor --- health/health_server.go | 47 +++++++++++++++++++++++++++++++++++++++++ health/health_test.go | 27 +++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 health/health_server.go create mode 100644 health/health_test.go diff --git a/health/health_server.go b/health/health_server.go new file mode 100644 index 0000000..4cb064f --- /dev/null +++ b/health/health_server.go @@ -0,0 +1,47 @@ +package health + +import ( + "context" + "net/http" + "strconv" + "time" + + "golang.org/x/exp/slog" +) + +// HealthCheckListener runs simple http server on the specified port for +// health check probes +func HealthCheckListener(ctx context.Context, port int, log *slog.Logger) { + log.Info("Starting health listener", "port", port) + + serveMux := http.NewServeMux() + + serveMux.HandleFunc("/__health", basicHealth) + + srv := &http.Server{ + Addr: ":" + strconv.Itoa(port), + ReadTimeout: 10 * time.Second, + WriteTimeout: 20 * time.Second, + IdleTimeout: 120 * time.Second, + Handler: serveMux, + } + + go func() { + err := srv.ListenAndServe() + if err != http.ErrServerClosed { + log.Warn("health check server done listening", "err", err) + } + }() + + <-ctx.Done() + + if err := srv.Shutdown(ctx); err != nil { + log.Error("health check server shutdown failed", "err", err) + } + +} + +func basicHealth(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Write([]byte("ok")) +} diff --git a/health/health_test.go b/health/health_test.go new file mode 100644 index 0000000..dfee501 --- /dev/null +++ b/health/health_test.go @@ -0,0 +1,27 @@ +package health + +import ( + "io" + "net/http" + "net/http/httptest" + "testing" +) + +func TestHealthHandler(t *testing.T) { + + req := httptest.NewRequest(http.MethodGet, "/__health", nil) + w := httptest.NewRecorder() + + basicHealth(w, req) + + res := w.Result() + defer res.Body.Close() + data, err := io.ReadAll(res.Body) + if err != nil { + t.Errorf("expected error to be nil got %v", err) + } + if string(data) != "ok" { + t.Fail() + t.Errorf("expected ok got %q", string(data)) + } +}