feat(T1.8): implement health checks for Kubernetes deployment
- Add internal/health package with health checker - Implement three endpoints: /health, /health/live, /health/ready - /health returns full JSON report with component status, latency, timestamp - /health/live for K8s liveness probe (service running) - /health/ready for K8s readiness probe (ready to accept traffic) - Temporal connectivity check via GetWorkflow call with timeout - Health check caching (30s interval) to prevent excessive checks - Graceful shutdown: health server stops on SIGINT/SIGTERM - Add --health flag to starter command to run health check - Worker runs health server on port 8081 alongside task queue worker - 10/10 unit tests passing - All verification criteria met Closes T1.8
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Handler provides HTTP endpoints for health checks
|
||||
type Handler struct {
|
||||
checker *Checker
|
||||
}
|
||||
|
||||
// NewHandler creates a new HTTP handler for health checks
|
||||
func NewHandler(checker *Checker) *Handler {
|
||||
return &Handler{
|
||||
checker: checker,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers health check routes on a mux
|
||||
func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/health", h.handleHealth)
|
||||
mux.HandleFunc("/health/live", h.handleLive)
|
||||
mux.HandleFunc("/health/ready", h.handleReady)
|
||||
}
|
||||
|
||||
// handleHealth returns full health report
|
||||
func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
report := h.checker.Check(r.Context())
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Return 200 if healthy, 503 if unhealthy
|
||||
if report.Status != StatusHealthy {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(report)
|
||||
}
|
||||
|
||||
// handleLive is Kubernetes liveness probe endpoint
|
||||
// Returns 200 if the service is running, 503 otherwise
|
||||
func (h *Handler) handleLive(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if h.checker.temporalClient == nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
w.Write([]byte("service not initialized"))
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("alive"))
|
||||
}
|
||||
|
||||
// handleReady is Kubernetes readiness probe endpoint
|
||||
// Returns 200 if the service is ready to accept traffic, 503 otherwise
|
||||
func (h *Handler) handleReady(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
report := h.checker.Check(r.Context())
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Service is ready only if healthy
|
||||
if report.Status != StatusHealthy {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"ready": report.Status == StatusHealthy,
|
||||
"components": report.Components,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user