diff --git a/utils/overall-health-check/Dockerfile b/utils/overall-health-check/Dockerfile new file mode 100644 index 0000000..118080c --- /dev/null +++ b/utils/overall-health-check/Dockerfile @@ -0,0 +1,27 @@ +# Start from a base image with Go installed +FROM golang:1.21-alpine as builder + +# Set the working directory inside the container +WORKDIR /app + +# Copy the Go module +COPY go.mod ./ + +# Download Go module dependencies +RUN go mod download + +# Copy the source code into the container +COPY main.go ./ + +# Compile the application +RUN go build -o healthcheck + +######################### RUNTIME ######################## +FROM scratch + +# Copy the Pre-built binary file from the previous stage +COPY --from=builder /app/healthcheck . +ENV PATH="/" + +# Run the executable +CMD ["./healthcheck"] diff --git a/utils/overall-health-check/go.mod b/utils/overall-health-check/go.mod new file mode 100644 index 0000000..1b4720f --- /dev/null +++ b/utils/overall-health-check/go.mod @@ -0,0 +1,3 @@ +module healthcheck + +go 1.21 diff --git a/utils/overall-health-check/main.go b/utils/overall-health-check/main.go new file mode 100644 index 0000000..2c8decc --- /dev/null +++ b/utils/overall-health-check/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "flag" + "fmt" + "io" + "log" + "net/http" + "regexp" +) + +func main() { + var metricsPort, apiPort int + flag.IntVar(&metricsPort, "metrics-port", 9090, "Port where Prometheus metrics are exposed") + flag.IntVar(&apiPort, "api-port", 8081, "Port to expose health check API") + flag.Parse() + + http.HandleFunc("/overall_health", func(w http.ResponseWriter, r *http.Request) { + healthCheckHandler(w, r, metricsPort) + }) + + log.Printf("Checking health on port %d", metricsPort) + log.Printf("Starting health check API server on port %d", apiPort) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", apiPort), nil)) +} + +func healthCheckHandler(w http.ResponseWriter, r *http.Request, metricsPort int) { + resp, err := http.Get(fmt.Sprintf("http://localhost:%d/metrics", metricsPort)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + re := regexp.MustCompile(`lava_consumer_overall_health (\d+)`) + matches := re.FindStringSubmatch(string(body)) + if len(matches) > 1 && matches[1] == "1" { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Healthy")) + } else { + http.Error(w, "Unhealthy", http.StatusServiceUnavailable) + } +}