From 8c67b44052ca9f43b580c5befbc2e46daba3022d Mon Sep 17 00:00:00 2001 From: Hector Date: Sat, 9 Sep 2023 06:15:23 +0000 Subject: [PATCH] feat: add health endpoint to exporter (!110) * Add new `/health` endpoint to check if exporter is running correctly * Update docker healthcheck to use the new endpoint https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/-/merge_requests/110 --- Dockerfile | 4 ++-- collector/f2b/collector.go | 16 ++++++++++++++++ exporter.go | 2 +- health | 2 +- server/handler.go | 11 +++++++++++ server/server.go | 7 +++++++ 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index c3e1f50..cd601d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ WORKDIR /app # Copy compiled binary to release image # (must build the binary before running docker build) COPY fail2ban_exporter /app/fail2ban_exporter -COPY health /app/health # Setup a healthcheck +COPY health /app/health RUN apk add curl -HEALTHCHECK --interval=10s --timeout=4s CMD /app/health +HEALTHCHECK --interval=10s --timeout=4s --retries=3 CMD /app/health ENTRYPOINT ["/app/fail2ban_exporter"] diff --git a/collector/f2b/collector.go b/collector/f2b/collector.go index e6a8ed8..d836d61 100644 --- a/collector/f2b/collector.go +++ b/collector/f2b/collector.go @@ -61,6 +61,22 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) { c.collectErrorCountMetric(ch) } +func (c *Collector) IsHealthy() bool { + s, err := socket.ConnectToSocket(c.socketPath) + if err != nil { + log.Printf("error opening socket: %v", err) + c.socketConnectionErrorCount++ + return false + } + pingSuccess, err := s.Ping() + if err != nil { + log.Printf("error pinging fail2ban server: %v", err) + c.socketRequestErrorCount++ + return false + } + return pingSuccess +} + func printFail2BanServerVersion(socketPath string) { s, err := socket.ConnectToSocket(socketPath) if err != nil { diff --git a/exporter.go b/exporter.go index 5dc6e0d..33554a7 100644 --- a/exporter.go +++ b/exporter.go @@ -44,7 +44,7 @@ func main() { prometheus.MustRegister(textFileCollector) if !appSettings.DryRunMode { - svrErr := server.StartServer(appSettings, textFileCollector) + svrErr := server.StartServer(appSettings, f2bCollector, textFileCollector) err := <-svrErr log.Fatal(err) } else { diff --git a/health b/health index 473ab31..89499b6 100755 --- a/health +++ b/health @@ -5,4 +5,4 @@ if [ ! -z $F2B_WEB_LISTEN_ADDRESS ]; then port=`echo $F2B_WEB_LISTEN_ADDRESS | cut -d ":" -f 2 -` fi -curl --fail localhost:$port/metrics || exit 1 +curl --fail localhost:$port/health || exit 1 diff --git a/server/handler.go b/server/handler.go index a0d8bf2..f463a19 100644 --- a/server/handler.go +++ b/server/handler.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/prometheus/client_golang/prometheus/promhttp" + "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/f2b" "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/textfile" ) @@ -31,3 +32,13 @@ func metricHandler(w http.ResponseWriter, r *http.Request, collector *textfile.C promhttp.Handler().ServeHTTP(w, r) collector.WriteTextFileMetrics(w, r) } + +func healthHandler(w http.ResponseWriter, r *http.Request, collector *f2b.Collector) { + if collector.IsHealthy() { + w.WriteHeader(http.StatusOK) + w.Write([]byte("{\"healthy\":true}")) + } else { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("{\"healthy\":false}")) + } +} diff --git a/server/server.go b/server/server.go index 315846e..1271dc6 100644 --- a/server/server.go +++ b/server/server.go @@ -6,11 +6,13 @@ import ( "time" "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/cfg" + "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/f2b" "gitlab.com/hectorjsmith/fail2ban-prometheus-exporter/collector/textfile" ) func StartServer( appSettings *cfg.AppSettings, + f2bCollector *f2b.Collector, textFileCollector *textfile.Collector, ) chan error { http.HandleFunc("/", AuthMiddleware( @@ -23,6 +25,11 @@ func StartServer( }, appSettings.AuthProvider, )) + http.HandleFunc("/health", + func(w http.ResponseWriter, r *http.Request) { + healthHandler(w, r, f2bCollector) + }, + ) log.Printf("metrics available at '%s'", metricsPath) svrErr := make(chan error)