WIP: EOL
This commit is contained in:
parent
7ed1e05284
commit
49e05cac10
23 changed files with 613 additions and 253 deletions
145
internal/server/server.go
Normal file
145
internal/server/server.go
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
fiberzap "github.com/gofiber/contrib/fiberzap/v2"
|
||||
"github.com/gofiber/contrib/swagger"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
middleware "github.com/oapi-codegen/fiber-middleware"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/api"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
ready ready
|
||||
}
|
||||
|
||||
func NewServer() *Server {
|
||||
return &Server{}
|
||||
}
|
||||
|
||||
func (s *Server) Run() {
|
||||
shutdownCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
logger.Fatal("failed to initialize logger", zap.Error(err))
|
||||
}
|
||||
defer logger.Sync()
|
||||
sugaredLogger := logger.Sugar()
|
||||
|
||||
fiberConfig := fiber.Config{
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
IdleTimeout: 5 * time.Second,
|
||||
|
||||
DisableStartupMessage: true,
|
||||
}
|
||||
|
||||
app := fiber.New(fiberConfig)
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
})
|
||||
|
||||
app.Get("/livez", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
})
|
||||
|
||||
app.Get("/readyz", func(c *fiber.Ctx) error {
|
||||
if s.ready.isReady() {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusServiceUnavailable)
|
||||
})
|
||||
|
||||
// TODO: add /metrics endpoint
|
||||
|
||||
app.Use(fiberzap.New(fiberzap.Config{
|
||||
Logger: logger,
|
||||
}))
|
||||
|
||||
swaggerConfig := swagger.Config{
|
||||
BasePath: "/",
|
||||
FilePath: "./api/v1/openapi.yaml",
|
||||
Path: "/swagger",
|
||||
Title: "Swagger API Docs",
|
||||
}
|
||||
|
||||
app.Use(swagger.New(swaggerConfig))
|
||||
|
||||
app = registerAPIHandlers(app, sugaredLogger)
|
||||
|
||||
<-shutdownCtx.Done()
|
||||
stop()
|
||||
s.ready.notReady()
|
||||
|
||||
// wait till Pod is signaling not ready (no new requests)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
shutdownBegan := time.Now()
|
||||
|
||||
sugaredLogger.Info("Shutdown signal recieved. Shutting down gracefully...")
|
||||
|
||||
// * Gracefully shut down fiber server
|
||||
// * If does not shutdown within `gracefulShutdownPeriod`, force shutdown
|
||||
// ! Does not close keep-alive connections
|
||||
// ! fiber.Config{ReadTimeout} must be set and non-zero and greater than the `gracefulShutdownPeriod`
|
||||
// TODO: make shutdown timeout configurable
|
||||
err = app.ShutdownWithTimeout(time.Second * 10)
|
||||
if err != nil {
|
||||
sugaredLogger.Errorw("Error during shutdown", "error", err)
|
||||
}
|
||||
|
||||
sugaredLogger.Infof("Shutdown completed in %v", time.Since(shutdownBegan))
|
||||
}
|
||||
|
||||
func registerAPIHandlers(fiberApp *fiber.App, logger *zap.SugaredLogger) *fiber.App {
|
||||
swagger, err := api.GetSwagger()
|
||||
if err != nil {
|
||||
logger.Fatalf("Error getting swagger: %v", err)
|
||||
}
|
||||
|
||||
// TODO: validate this. Copied from example
|
||||
// See: https://github.com/deepmap/oapi-codegen/blob/master/examples/petstore-expanded/fiber/petstore.go#L41
|
||||
// Clear out the servers array in the swagger spec, that skips validating
|
||||
// that server names match. We don't know how this thing will be run.
|
||||
swagger.Servers = nil
|
||||
|
||||
fiberApp.Use(middleware.OapiRequestValidator(swagger))
|
||||
|
||||
api.RegisterHandlers(fiberApp)
|
||||
|
||||
return fiberApp
|
||||
}
|
||||
|
||||
type ready struct {
|
||||
ready bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (r *ready) setReady() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.ready = true
|
||||
}
|
||||
|
||||
func (r *ready) isReady() bool {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
return r.ready
|
||||
}
|
||||
|
||||
func (r *ready) notReady() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.ready = false
|
||||
}
|
||||
Reference in a new issue