@@ -21,41 +21,89 @@ import (
2121 "errors"
2222 "net"
2323 "net/http"
24+ "time"
2425
25- "github.com/go-logr/logr "
26+ crlog "sigs.k8s.io/controller-runtime/pkg/log "
2627)
2728
28- // server is a general purpose HTTP server Runnable for a manager
29- // to serve some internal handlers such as health probes, metrics and profiling.
30- type server struct {
31- Kind string
32- Log logr.Logger
33- Server * http.Server
29+ var (
30+ _ Runnable = (* Server )(nil )
31+ _ LeaderElectionRunnable = (* Server )(nil )
32+ )
33+
34+ // Server is a general purpose HTTP server Runnable for a manager.
35+ // It is used to serve some internal handlers for health probes and profiling,
36+ // but it can also be used to run custom servers.
37+ type Server struct {
38+ // Name is an optional string that describes the purpose of the server. It is used in logs to distinguish
39+ // among multiple servers.
40+ Name string
41+
42+ // Server is the HTTP server to run. It is required.
43+ Server * http.Server
44+
45+ // Listener is an optional listener to use. If not set, the server start a listener using the server.Addr.
46+ // Using a listener is useful when the port reservation needs to happen in advance of this runnable starting.
3447 Listener net.Listener
48+
49+ // OnlyServeWhenLeader is an optional bool that indicates that the server should only be started when the manager is the leader.
50+ OnlyServeWhenLeader bool
51+
52+ // ShutdownTimeout is an optional duration that indicates how long to wait for the server to shutdown gracefully. If not set,
53+ // the server will wait indefinitely for all connections to close.
54+ ShutdownTimeout * time.Duration
3555}
3656
37- func (s * server ) Start (ctx context.Context ) error {
38- log := s .Log .WithValues ("kind" , s .Kind , "addr" , s .Listener .Addr ())
57+ // Start starts the server. It will block until the server is stopped or an error occurs.
58+ func (s * Server ) Start (ctx context.Context ) error {
59+ log := crlog .FromContext (ctx )
60+ if s .Name != "" {
61+ log = log .WithValues ("name" , s .Name )
62+ }
63+ log = log .WithValues ("addr" , s .addr ())
3964
4065 serverShutdown := make (chan struct {})
4166 go func () {
4267 <- ctx .Done ()
4368 log .Info ("shutting down server" )
44- if err := s .Server .Shutdown (context .Background ()); err != nil {
69+
70+ shutdownCtx := context .Background ()
71+ if s .ShutdownTimeout != nil {
72+ var shutdownCancel context.CancelFunc
73+ shutdownCtx , shutdownCancel = context .WithTimeout (context .Background (), * s .ShutdownTimeout )
74+ defer shutdownCancel ()
75+ }
76+
77+ if err := s .Server .Shutdown (shutdownCtx ); err != nil {
4578 log .Error (err , "error shutting down server" )
4679 }
4780 close (serverShutdown )
4881 }()
4982
5083 log .Info ("starting server" )
51- if err := s .Server . Serve ( s . Listener ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
84+ if err := s .serve ( ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
5285 return err
5386 }
5487
5588 <- serverShutdown
5689 return nil
5790}
5891
59- func (s * server ) NeedLeaderElection () bool {
60- return false
92+ // NeedLeaderElection returns true if the server should only be started when the manager is the leader.
93+ func (s * Server ) NeedLeaderElection () bool {
94+ return s .OnlyServeWhenLeader
95+ }
96+
97+ func (s * Server ) addr () string {
98+ if s .Listener != nil {
99+ return s .Listener .Addr ().String ()
100+ }
101+ return s .Server .Addr
102+ }
103+
104+ func (s * Server ) serve () error {
105+ if s .Listener != nil {
106+ return s .Server .Serve (s .Listener )
107+ }
108+ return s .Server .ListenAndServe ()
61109}
0 commit comments