Skip to content

Commit a20d09d

Browse files
committed
refactor: addition of a generic server to use for health, metrics etc
1 parent f35715d commit a20d09d

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"net/http"
8+
"strconv"
9+
"time"
10+
11+
logger "k8s.io/klog/v2"
12+
)
13+
14+
type ServerOption func(*Server)
15+
16+
// WithTimeout sets the shutdown timeout for the server.
17+
func WithTimeout(timeout time.Duration) ServerOption {
18+
return func(s *Server) {
19+
s.shutdownTimeout = timeout
20+
}
21+
}
22+
23+
type Server struct {
24+
httpServer http.Server
25+
listener net.Listener
26+
endpoint string
27+
shutdownTimeout time.Duration
28+
}
29+
30+
func NewServer(port int, endpoint string, handler http.Handler, options ...ServerOption) (*Server, error) {
31+
addr := "0"
32+
if port != 0 {
33+
addr = ":" + strconv.Itoa(port)
34+
}
35+
36+
listener, err := newListener(addr)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
mux := http.NewServeMux()
42+
mux.Handle(endpoint, handler)
43+
44+
s := &Server{
45+
endpoint: endpoint,
46+
listener: listener,
47+
httpServer: http.Server{Handler: mux},
48+
shutdownTimeout: 30 * time.Second, // Default value
49+
}
50+
51+
for _, opt := range options {
52+
opt(s)
53+
}
54+
55+
return s, nil
56+
}
57+
58+
func (s *Server) Start() (err error) {
59+
if s.listener == nil {
60+
logger.Infof("Serving endpoint %s is disabled", s.endpoint)
61+
return
62+
}
63+
64+
defer func() {
65+
if r := recover(); r != nil {
66+
err = fmt.Errorf("serving endpoint %s failed: %v", s.endpoint, r)
67+
}
68+
}()
69+
70+
logger.Infof("Started serving endpoint %s at %s", s.endpoint, s.listener.Addr())
71+
if e := s.httpServer.Serve(s.listener); e != http.ErrServerClosed {
72+
return fmt.Errorf("serving endpoint %s failed: %v", s.endpoint, e)
73+
}
74+
return
75+
}
76+
77+
func (s *Server) Shutdown() error {
78+
if s.listener == nil {
79+
return nil
80+
}
81+
82+
logger.Info("Stopping server")
83+
84+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
85+
defer cancel()
86+
87+
// Try graceful shutdown
88+
if err := s.httpServer.Shutdown(shutdownCtx); err != nil {
89+
return fmt.Errorf("failed to shutdown server gracefully: %v", err)
90+
}
91+
return s.httpServer.Shutdown(shutdownCtx)
92+
}
93+
94+
// newListener creates a new TCP listener bound to the given address.
95+
func newListener(addr string) (net.Listener, error) {
96+
// Add a case to disable serving altogether
97+
if addr == "0" {
98+
return nil, nil
99+
}
100+
101+
listener, err := net.Listen("tcp", addr)
102+
if err != nil {
103+
return nil, fmt.Errorf("failed to create listener: %v", err)
104+
}
105+
106+
return listener, nil
107+
}

0 commit comments

Comments
 (0)