1
2
3 package server
4
5 import (
6 "context"
7 "fmt"
8 "net"
9 "net/http"
10 "sync"
11 "time"
12
13 "github.com/gin-gonic/gin"
14 "github.com/prometheus/client_golang/prometheus/promhttp"
15 )
16
17 func init() {
18 gin.SetMode(gin.ReleaseMode)
19 gin.DisableConsoleColor()
20 }
21
22
23 type Handler func(c *gin.Context)
24
25
26
27 type Config struct {
28 port int
29 listener net.Listener
30
31 handlers map[string]Handler
32
33 secure bool
34 secureCertFile string
35 secureKeyFile string
36 }
37
38
39 func (c Config) Validate() error {
40 if c.port != 0 && c.listener != nil {
41 return fmt.Errorf("OptionPort and OptionListener cannot both be set")
42 }
43
44 if c.secure && c.listener != nil {
45 return fmt.Errorf("OptionSecure can not be set with OptionListener")
46 }
47
48 return nil
49 }
50
51
52 func (c Config) Addr() string {
53 if c.port == 0 {
54 if c.secure {
55 return ":443"
56 }
57 return ":80"
58 }
59 return fmt.Sprintf(":%d", c.port)
60 }
61
62
63 type Server struct {
64 Router *gin.Engine
65
66 Config Config
67
68
69 shutdownFunc func(context.Context) error
70 }
71
72
73
74
75
76
77
78
79 func NewServer(options ...Option) (*Server, error) {
80 var s Server
81 s.Router = gin.New()
82
83 for _, option := range options {
84 if err := option(&s.Config); err != nil {
85 return nil, err
86 }
87 }
88 if err := s.Config.Validate(); err != nil {
89 return nil, err
90 }
91
92 for path, h := range s.Config.handlers {
93 s.Router.GET(path, gin.HandlerFunc(h))
94 }
95
96 return &s, nil
97 }
98
99
100 func NewHealthServer(opts ...Option) (*Server, error) {
101 var h = func(c *gin.Context) {
102 c.JSON(http.StatusOK, "UP")
103 }
104
105
106 var alloptscopy = []Option{
107 OptionHandler("/healthz", h),
108 }
109 alloptscopy = append(alloptscopy, opts...)
110
111 return NewServer(alloptscopy...)
112 }
113
114
115 func NewReadyServer(opts ...Option) (s *Server, ready func(), err error) {
116 var ch = make(chan bool)
117 ready = func() {
118 close(ch)
119 }
120 var h = func(c *gin.Context) {
121 select {
122 case <-ch:
123
124 c.JSON(http.StatusOK, "READY")
125 default:
126 c.JSON(http.StatusServiceUnavailable, "NOT READY")
127 }
128 }
129
130 var alloptscopy = []Option{
131 OptionHandler("/readyz", h),
132 }
133 alloptscopy = append(alloptscopy, opts...)
134
135 s, err = NewServer(alloptscopy...)
136 if err != nil {
137 close(ch)
138 return nil, nil, err
139 }
140 return s, ready, err
141 }
142
143 var PrometheusMetricsHandler = gin.WrapH(promhttp.Handler())
144
145
146
147
148 func NewMetricsServer(opts ...Option) (*Server, error) {
149 var oh = OptionHandler("/metrics", PrometheusMetricsHandler)
150 s, ready, err := NewReadyServer(append([]Option{oh}, opts...)...)
151 if err != nil {
152 return nil, err
153 }
154 ready()
155 return s, err
156 }
157
158
159
160
161 func NewLivenessServer(opts ...Option) (s *Server, setLivenessStatus func(code int), err error) {
162
163 var liveValue struct {
164 sync.Mutex
165 code int
166 }
167
168 var set = func(code int) {
169 liveValue.Lock()
170 liveValue.code = code
171 liveValue.Unlock()
172 }
173
174 var h = func(c *gin.Context) {
175 liveValue.Lock()
176 var code = liveValue.code
177 liveValue.Unlock()
178
179
180 if code == 0 {
181 code = http.StatusServiceUnavailable
182 }
183
184 if code >= 400 {
185 c.JSON(code, "DOWN")
186 } else {
187 c.JSON(code, "LIVE")
188 }
189 }
190
191
192 var alloptscopy = []Option{
193 OptionHandler("/livez", h),
194 }
195 alloptscopy = append(alloptscopy, opts...)
196
197 s, err = NewServer(alloptscopy...)
198 if err != nil {
199 return nil, nil, err
200 }
201 return s, set, nil
202 }
203
204
205
206
207 var ReadHeaderTimeout = time.Minute
208
209
210 func (s *Server) Run() error {
211
212 var hs = &http.Server{
213 Handler: s.Router,
214 Addr: s.Config.Addr(),
215 ReadHeaderTimeout: ReadHeaderTimeout,
216 }
217 s.shutdownFunc = hs.Shutdown
218
219 if s.Config.listener != nil {
220
221 return hs.Serve(s.Config.listener)
222 } else if s.Config.secure {
223
224 return hs.ListenAndServeTLS(s.Config.secureCertFile, s.Config.secureKeyFile)
225 }
226
227 return hs.ListenAndServe()
228 }
229
230
231 func (s *Server) Shutdown(ctx context.Context) error {
232 if s.shutdownFunc == nil {
233 return nil
234 }
235 return s.shutdownFunc(ctx)
236 }
237
View as plain text