-
Notifications
You must be signed in to change notification settings - Fork 129
/
httpserver.go
108 lines (89 loc) · 2.9 KB
/
httpserver.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package server
import (
"context"
"encoding/json"
"time"
"github.com/gofiber/adaptor/v2"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/pprof"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
"github.com/kubeshop/testkube/pkg/log"
"github.com/kubeshop/testkube/pkg/problem"
)
// NewServer returns new HTTP server instance, initializes logger and metrics
func NewServer(config Config) HTTPServer {
config.Http.DisableStartupMessage = true
s := HTTPServer{
Mux: fiber.New(config.Http),
Log: log.DefaultLogger,
Config: config,
}
s.Init()
return s
}
// HTTPServer represents basic REST HTTP server abstarction
type HTTPServer struct {
Mux *fiber.App
Log *zap.SugaredLogger
Routes fiber.Router
Config Config
}
// Init initializes router and setting up basic routes for health and metrics
func (s *HTTPServer) Init() {
// global log for requests
s.Mux.Use(func(c *fiber.Ctx) error {
s.Log.Debugw("request", "method", string(c.Request().Header.Method()), "path", c.Request().URI().String())
return c.Next()
})
s.Mux.Use(pprof.New())
// server generic endpoints
s.Mux.Get("/health", s.HealthEndpoint())
s.Mux.Get("/metrics", adaptor.HTTPHandler(promhttp.Handler()))
// v1 API
v1 := s.Mux.Group("/v1")
v1.Static("/api-docs", "./api/v1")
s.Routes = v1
}
// Warn writes RFC-7807 json problem to response
func (s *HTTPServer) Warn(c *fiber.Ctx, status int, err error, context ...interface{}) error {
c.Status(status)
c.Response().Header.Set("Content-Type", "application/problem+json")
s.Log.Warnw(err.Error(), "status", status)
pr := problem.New(status, s.getProblemMessage(err, context))
return c.JSON(pr)
}
// Error writes RFC-7807 json problem to response
func (s *HTTPServer) Error(c *fiber.Ctx, status int, err error, context ...interface{}) error {
c.Status(status)
c.Response().Header.Set("Content-Type", "application/problem+json")
s.Log.Errorw(err.Error(), "status", status)
pr := problem.New(status, s.getProblemMessage(err, context))
return c.JSON(pr)
}
// getProblemMessage creates new JSON based problem message and returns it as string
func (s *HTTPServer) getProblemMessage(err error, context ...interface{}) string {
message := err.Error()
if len(context) > 0 {
b, err := json.Marshal(context[0])
if err == nil {
message += ", context: " + string(b)
}
}
return message
}
// Run starts listening for incoming connetions
func (s *HTTPServer) Run(ctx context.Context) error {
// this function listens for finished context and calls graceful shutdown on the API server
go func() {
select {
case <-ctx.Done():
// sleep 2 seconds to cover the edge case if SIGTERM or SIGKILL signal occurs before the server is started,
// so the application does not get stuck
time.Sleep(2 * time.Second)
s.Log.Infof("shutting down Testkube API server")
_ = s.Mux.Shutdown()
}
}()
return s.Mux.Listen(s.Config.Addr())
}