@@ -21,41 +21,89 @@ import (
21
21
"errors"
22
22
"net"
23
23
"net/http"
24
+ "time"
24
25
25
- "github.com/go-logr/logr "
26
+ crlog "sigs.k8s.io/controller-runtime/pkg/log "
26
27
)
27
28
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.
34
47
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
35
55
}
36
56
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 ())
39
64
40
65
serverShutdown := make (chan struct {})
41
66
go func () {
42
67
<- ctx .Done ()
43
68
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 {
45
78
log .Error (err , "error shutting down server" )
46
79
}
47
80
close (serverShutdown )
48
81
}()
49
82
50
83
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 ) {
52
85
return err
53
86
}
54
87
55
88
<- serverShutdown
56
89
return nil
57
90
}
58
91
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 ()
61
109
}
0 commit comments