Implement readiness endpoint (/api/ready) that returns:
- {"ready":true} (HTTP 200) during normal operation
- {"ready":false} (HTTP 503) during graceful shutdown
Key changes:
- Added readiness context to control readiness state
- Modified server.NewServer() to accept readiness context
- Implemented handleReadiness() with context-aware logic
- Updated cmd/server/main.go to manage readiness state
- Readiness set to false when shutdown signal received
- Updated test script to validate readiness behavior
- Added comprehensive documentation for readiness endpoint
This allows Kubernetes/service meshes to stop routing traffic
to the pod during graceful shutdown while allowing existing
requests to complete. Health endpoint continues to return
happy status during shutdown for proper orchestration.
86 lines
1.9 KiB
Go
86 lines
1.9 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"DanceLessonsCoach/pkg/config"
|
|
"DanceLessonsCoach/pkg/greet"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Server struct {
|
|
router *chi.Mux
|
|
readyCtx context.Context
|
|
}
|
|
|
|
func NewServer(cfg *config.Config, readyCtx context.Context) *Server {
|
|
s := &Server{
|
|
router: chi.NewRouter(),
|
|
readyCtx: readyCtx,
|
|
}
|
|
s.setupRoutes()
|
|
return s
|
|
}
|
|
|
|
func (s *Server) setupRoutes() {
|
|
// Use Zerolog middleware instead of Chi's default logger
|
|
s.router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{
|
|
Logger: &log.Logger,
|
|
NoColor: false,
|
|
}))
|
|
|
|
// Health endpoint at root level
|
|
s.router.Get("/api/health", s.handleHealth)
|
|
|
|
// Readiness endpoint at root level
|
|
s.router.Get("/api/ready", s.handleReadiness)
|
|
|
|
// API routes
|
|
s.router.Route("/api/v1", func(r chi.Router) {
|
|
r.Use(s.apiMiddlewares()...)
|
|
s.registerApiV1Routes(r)
|
|
})
|
|
}
|
|
|
|
func (s *Server) registerApiV1Routes(r chi.Router) {
|
|
greetService := greet.NewService()
|
|
greetHandler := greet.NewApiV1GreetHandler(greetService)
|
|
r.Route("/greet", func(r chi.Router) {
|
|
greetHandler.RegisterRoutes(r)
|
|
})
|
|
}
|
|
|
|
func (s *Server) apiMiddlewares() []func(http.Handler) http.Handler {
|
|
return []func(http.Handler) http.Handler{
|
|
middleware.StripSlashes,
|
|
middleware.Recoverer,
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
|
log.Info().Msg("Health check requested")
|
|
w.Write([]byte(`{"status":"healthy"}`))
|
|
}
|
|
|
|
func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) {
|
|
log.Info().Msg("Readiness check requested")
|
|
|
|
select {
|
|
case <-s.readyCtx.Done():
|
|
log.Info().Msg("Readiness check: not ready (shutting down)")
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write([]byte(`{"ready":false}`))
|
|
default:
|
|
log.Info().Msg("Readiness check: ready")
|
|
w.Write([]byte(`{"ready":true}`))
|
|
}
|
|
}
|
|
|
|
func (s *Server) Router() http.Handler {
|
|
return s.router
|
|
}
|