@@ -20,6 +20,7 @@ import (
20
20
"context"
21
21
"fmt"
22
22
"io/ioutil"
23
+ "net/http"
23
24
"os"
24
25
"regexp"
25
26
"strings"
@@ -39,6 +40,14 @@ const (
39
40
defaultLeaseDuration = 15 * time .Second
40
41
defaultRenewDeadline = 10 * time .Second
41
42
defaultRetryPeriod = 5 * time .Second
43
+
44
+ DefaultHealthCheckTimeout = 20 * time .Second
45
+
46
+ // HealthCheckerAddress is the address at which the leader election health
47
+ // checker reports status.
48
+ // The caller sidecar should document this address in appropriate flag
49
+ // descriptions.
50
+ HealthCheckerAddress = "/healthz/leader-election"
42
51
)
43
52
44
53
// leaderElection is a convenience wrapper around client-go's leader election library.
@@ -55,6 +64,9 @@ type leaderElection struct {
55
64
// valid options are resourcelock.LeasesResourceLock, resourcelock.EndpointsResourceLock,
56
65
// and resourcelock.ConfigMapsResourceLock
57
66
resourceLock string
67
+ // healthCheck reports unhealthy if leader election fails to renew leadership
68
+ // within a timeout period.
69
+ healthCheck * leaderelection.HealthzAdaptor
58
70
59
71
leaseDuration time.Duration
60
72
renewDeadline time.Duration
@@ -71,6 +83,8 @@ func NewLeaderElection(clientset kubernetes.Interface, lockName string, runFunc
71
83
}
72
84
73
85
// NewLeaderElectionWithLeases returns an implementation of leader election using Leases
86
+ // healthCheckTimeout determines the max duration beyond lease expiration
87
+ // allowed before reporting unhealthy.
74
88
func NewLeaderElectionWithLeases (clientset kubernetes.Interface , lockName string , runFunc func (ctx context.Context )) * leaderElection {
75
89
return & leaderElection {
76
90
runFunc : runFunc ,
@@ -84,6 +98,8 @@ func NewLeaderElectionWithLeases(clientset kubernetes.Interface, lockName string
84
98
}
85
99
86
100
// NewLeaderElectionWithEndpoints returns an implementation of leader election using Endpoints
101
+ // healthCheckTimeout determines the max duration beyond lease expiration
102
+ // allowed before reporting unhealthy.
87
103
func NewLeaderElectionWithEndpoints (clientset kubernetes.Interface , lockName string , runFunc func (ctx context.Context )) * leaderElection {
88
104
return & leaderElection {
89
105
runFunc : runFunc ,
@@ -97,6 +113,8 @@ func NewLeaderElectionWithEndpoints(clientset kubernetes.Interface, lockName str
97
113
}
98
114
99
115
// NewLeaderElectionWithConfigMaps returns an implementation of leader election using ConfigMaps
116
+ // healthCheckTimeout determines the max duration beyond lease expiration
117
+ // allowed before reporting unhealthy.
100
118
func NewLeaderElectionWithConfigMaps (clientset kubernetes.Interface , lockName string , runFunc func (ctx context.Context )) * leaderElection {
101
119
return & leaderElection {
102
120
runFunc : runFunc ,
@@ -134,6 +152,27 @@ func (l *leaderElection) WithContext(ctx context.Context) {
134
152
l .ctx = ctx
135
153
}
136
154
155
+ // Server represents any type that could serve HTTP requests for the leader
156
+ // election health check endpoint.
157
+ type Server interface {
158
+ Handle (pattern string , handler http.Handler )
159
+ }
160
+
161
+ // PrepareHealthCheck creates a health check for this leader election object
162
+ // with the given healthCheckTimeout and registers its HTTP handler to the given
163
+ // server at the path specified by the constant "healthCheckerAddress".
164
+ // healthCheckTimeout determines the max duration beyond lease expiration
165
+ // allowed before reporting unhealthy.
166
+ // The caller sidecar should document the handler address in appropriate flag
167
+ // descriptions.
168
+ func (l * leaderElection ) PrepareHealthCheck (
169
+ s Server ,
170
+ healthCheckTimeout time.Duration ) {
171
+
172
+ l .healthCheck = leaderelection .NewLeaderHealthzAdaptor (healthCheckTimeout )
173
+ s .Handle (HealthCheckerAddress , adaptCheckToHandler (l .healthCheck .Check ))
174
+ }
175
+
137
176
func (l * leaderElection ) Run () error {
138
177
if l .identity == "" {
139
178
id , err := defaultLeaderElectionIdentity ()
@@ -179,6 +218,7 @@ func (l *leaderElection) Run() error {
179
218
klog .V (3 ).Infof ("new leader detected, current leader: %s" , identity )
180
219
},
181
220
},
221
+ WatchDog : l .healthCheck ,
182
222
}
183
223
184
224
ctx := l .ctx
@@ -220,3 +260,15 @@ func inClusterNamespace() string {
220
260
221
261
return "default"
222
262
}
263
+
264
+ // adaptCheckToHandler returns an http.HandlerFunc that serves the provided checks.
265
+ func adaptCheckToHandler (c func (r * http.Request ) error ) http.HandlerFunc {
266
+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
267
+ err := c (r )
268
+ if err != nil {
269
+ http .Error (w , fmt .Sprintf ("internal server error: %v" , err ), http .StatusInternalServerError )
270
+ } else {
271
+ fmt .Fprint (w , "ok" )
272
+ }
273
+ })
274
+ }
0 commit comments