@@ -20,6 +20,7 @@ import (
2020 "context"
2121 "fmt"
2222 "io/ioutil"
23+ "net/http"
2324 "os"
2425 "regexp"
2526 "strings"
@@ -39,6 +40,14 @@ const (
3940 defaultLeaseDuration = 15 * time .Second
4041 defaultRenewDeadline = 10 * time .Second
4142 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"
4251)
4352
4453// leaderElection is a convenience wrapper around client-go's leader election library.
@@ -55,6 +64,9 @@ type leaderElection struct {
5564 // valid options are resourcelock.LeasesResourceLock, resourcelock.EndpointsResourceLock,
5665 // and resourcelock.ConfigMapsResourceLock
5766 resourceLock string
67+ // healthCheck reports unhealthy if leader election fails to renew leadership
68+ // within a timeout period.
69+ healthCheck * leaderelection.HealthzAdaptor
5870
5971 leaseDuration time.Duration
6072 renewDeadline time.Duration
@@ -134,6 +146,27 @@ func (l *leaderElection) WithContext(ctx context.Context) {
134146 l .ctx = ctx
135147}
136148
149+ // Server represents any type that could serve HTTP requests for the leader
150+ // election health check endpoint.
151+ type Server interface {
152+ Handle (pattern string , handler http.Handler )
153+ }
154+
155+ // PrepareHealthCheck creates a health check for this leader election object
156+ // with the given healthCheckTimeout and registers its HTTP handler to the given
157+ // server at the path specified by the constant "healthCheckerAddress".
158+ // healthCheckTimeout determines the max duration beyond lease expiration
159+ // allowed before reporting unhealthy.
160+ // The caller sidecar should document the handler address in appropriate flag
161+ // descriptions.
162+ func (l * leaderElection ) PrepareHealthCheck (
163+ s Server ,
164+ healthCheckTimeout time.Duration ) {
165+
166+ l .healthCheck = leaderelection .NewLeaderHealthzAdaptor (healthCheckTimeout )
167+ s .Handle (HealthCheckerAddress , adaptCheckToHandler (l .healthCheck .Check ))
168+ }
169+
137170func (l * leaderElection ) Run () error {
138171 if l .identity == "" {
139172 id , err := defaultLeaderElectionIdentity ()
@@ -179,6 +212,7 @@ func (l *leaderElection) Run() error {
179212 klog .V (3 ).Infof ("new leader detected, current leader: %s" , identity )
180213 },
181214 },
215+ WatchDog : l .healthCheck ,
182216 }
183217
184218 ctx := l .ctx
@@ -220,3 +254,15 @@ func inClusterNamespace() string {
220254
221255 return "default"
222256}
257+
258+ // adaptCheckToHandler returns an http.HandlerFunc that serves the provided checks.
259+ func adaptCheckToHandler (c func (r * http.Request ) error ) http.HandlerFunc {
260+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
261+ err := c (r )
262+ if err != nil {
263+ http .Error (w , fmt .Sprintf ("internal server error: %v" , err ), http .StatusInternalServerError )
264+ } else {
265+ fmt .Fprint (w , "ok" )
266+ }
267+ })
268+ }
0 commit comments