package cushion import ( "fmt" "net" "net/http" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus/promhttp" ) // todo - stolen from chariot which also has a server_test.go func init() { gin.SetMode(gin.ReleaseMode) gin.DisableConsoleColor() } // ServerConfig contains unexported configuration parameters for a Chariot server. // The fields are set using ServerOption functions. type ServerConfig struct { port int listener net.Listener } // ServerOption functions set unexported private fields in the ServerConfig. // Use it to set things like the storage backend and port. type ServerOption func(*ServerConfig) error // ServerOptionPort sets the port to listen on. Omitting the ServerOptionPort causes the server to use port 80. func ServerOptionPort(port int) ServerOption { return func(cfg *ServerConfig) error { if port <= 0 || port > 65535 { return fmt.Errorf("Invalid port number %q", port) } cfg.port = port return nil } } // ServerOptionListener is used to set a custom net.Listener for the server. // This server option is used in tests because some CICD environments don't allow ports to be opened. func ServerOptionListener(l net.Listener) ServerOption { return func(cfg *ServerConfig) error { cfg.listener = l return nil } } // Server represents the Chariot HTTP server. type Server struct { Router *gin.Engine Config ServerConfig } // HealthzEndpoints contains the Server type's http health endpoints for k8s that returns 200 OK. var HealthzEndpoints = []string{"/healthz", "/cushion/healthz"} // MetricsEndpoints contains the Server type's http endpoints for time series metrics (prometheus). var MetricsEndpoints = []string{"/metrics", "/cushion/metrics"} // NewServer creates a Chariot server that runs on the desired port. func NewServer(options ...ServerOption) (*Server, error) { s := &Server{ Router: gin.New(), } for _, option := range options { if err := option(&s.Config); err != nil { return nil, err } } // Set port to default HTTP port 80 if not provided as an option. if s.Config.port == 0 { s.Config.port = 80 } // healthz endpoints for _, hzep := range HealthzEndpoints { s.Router.GET(hzep, s.HealthHandlerFunction) } // metrics (prometheus) endpoints metricsHandlerFunc := gin.WrapH(promhttp.Handler()) for _, mep := range MetricsEndpoints { s.Router.GET(mep, metricsHandlerFunc) } return s, nil } // HealthHandlerFunction returns status `200 OK`. func (s *Server) HealthHandlerFunction(c *gin.Context) { c.JSON(http.StatusOK, "UP") } // Run runs the Gin server on the configured port or listener. func (s *Server) Run() error { if s.Config.listener != nil { return s.Router.RunListener(s.Config.listener) } addr := fmt.Sprintf(":%d", s.Config.port) return s.Router.Run(addr) }