@@ -21,41 +21,97 @@ import (
21
21
"errors"
22
22
"net"
23
23
"net/http"
24
+ "time"
24
25
25
26
"github.com/go-logr/logr"
27
+
28
+ crlog "sigs.k8s.io/controller-runtime/pkg/log"
29
+ )
30
+
31
+ var (
32
+ _ Runnable = (* Server )(nil )
33
+ _ LeaderElectionRunnable = (* Server )(nil )
26
34
)
27
35
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
36
+ // Server is a general purpose HTTP server Runnable for a manager.
37
+ // It is used to serve some internal handlers for health probes and profiling,
38
+ // but it can also be used to run custom servers.
39
+ type Server struct {
40
+ // Kind is an optional string that describes the purpose of the server. It is used in logs to distinguish
41
+ // among multiple servers.
42
+ Kind string
43
+
44
+ // Log is the logger used by the server. If not set, a logger will be derived from the context passed to Start.
45
+ Log logr.Logger
46
+
47
+ // Server is the HTTP server to run. It is required.
48
+ Server * http.Server
49
+
50
+ // Listener is an optional listener to use. If not set, the server start a listener using the server.Addr.
51
+ // Using a listener is useful when the port reservation needs to happen in advance of this runnable starting.
34
52
Listener net.Listener
53
+
54
+ // OnlyServeWhenLeader is an optional bool that indicates that the server should only be started when the manager is the leader.
55
+ OnlyServeWhenLeader bool
56
+
57
+ // ShutdownTimeout is an optional duration that indicates how long to wait for the server to shutdown gracefully. If not set,
58
+ // the server will wait indefinitely for all connections to close.
59
+ ShutdownTimeout * time.Duration
35
60
}
36
61
37
- func (s * server ) Start (ctx context.Context ) error {
38
- log := s .Log .WithValues ("kind" , s .Kind , "addr" , s .Listener .Addr ())
62
+ // Start starts the server. It will block until the server is stopped or an error occurs.
63
+ func (s * Server ) Start (ctx context.Context ) error {
64
+ log := s .Log
65
+ if log .GetSink () == nil {
66
+ log = crlog .FromContext (ctx )
67
+ }
68
+ if s .Kind != "" {
69
+ log = log .WithValues ("kind" , s .Kind )
70
+ }
71
+ log = log .WithValues ("addr" , s .addr ())
39
72
40
73
serverShutdown := make (chan struct {})
41
74
go func () {
42
75
<- ctx .Done ()
43
76
log .Info ("shutting down server" )
44
- if err := s .Server .Shutdown (context .Background ()); err != nil {
77
+
78
+ shutdownCtx := context .Background ()
79
+ if s .ShutdownTimeout != nil {
80
+ var shutdownCancel context.CancelFunc
81
+ shutdownCtx , shutdownCancel = context .WithTimeout (context .Background (), * s .ShutdownTimeout )
82
+ defer shutdownCancel ()
83
+ }
84
+
85
+ if err := s .Server .Shutdown (shutdownCtx ); err != nil {
45
86
log .Error (err , "error shutting down server" )
46
87
}
47
88
close (serverShutdown )
48
89
}()
49
90
50
91
log .Info ("starting server" )
51
- if err := s .Server . Serve ( s . Listener ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
92
+ if err := s .serve ( ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
52
93
return err
53
94
}
54
95
55
96
<- serverShutdown
56
97
return nil
57
98
}
58
99
59
- func (s * server ) NeedLeaderElection () bool {
60
- return false
100
+ // NeedLeaderElection returns true if the server should only be started when the manager is the leader.
101
+ func (s * Server ) NeedLeaderElection () bool {
102
+ return s .OnlyServeWhenLeader
103
+ }
104
+
105
+ func (s * Server ) addr () string {
106
+ if s .Listener != nil {
107
+ return s .Listener .Addr ().String ()
108
+ }
109
+ return s .Server .Addr
110
+ }
111
+
112
+ func (s * Server ) serve () error {
113
+ if s .Listener != nil {
114
+ return s .Server .Serve (s .Listener )
115
+ }
116
+ return s .Server .ListenAndServe ()
61
117
}
0 commit comments