@@ -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
@@ -66,16 +78,33 @@ type leaderElection struct {
66
78
}
67
79
68
80
// NewLeaderElection returns the default & preferred leader election type
69
- func NewLeaderElection (clientset kubernetes.Interface , lockName string , runFunc func (ctx context.Context )) * leaderElection {
70
- return NewLeaderElectionWithLeases (clientset , lockName , runFunc )
81
+ // healthCheckTimeout determines the max duration beyond lease expiration
82
+ // allowed before reporting unhealthy.
83
+ func NewLeaderElection (
84
+ clientset kubernetes.Interface ,
85
+ lockName string ,
86
+ healthCheckTimeout time.Duration ,
87
+ runFunc func (ctx context.Context )) * leaderElection {
88
+ return NewLeaderElectionWithLeases (
89
+ clientset ,
90
+ lockName ,
91
+ healthCheckTimeout ,
92
+ runFunc )
71
93
}
72
94
73
95
// NewLeaderElectionWithLeases returns an implementation of leader election using Leases
74
- func NewLeaderElectionWithLeases (clientset kubernetes.Interface , lockName string , runFunc func (ctx context.Context )) * leaderElection {
96
+ // healthCheckTimeout determines the max duration beyond lease expiration
97
+ // allowed before reporting unhealthy.
98
+ func NewLeaderElectionWithLeases (
99
+ clientset kubernetes.Interface ,
100
+ lockName string ,
101
+ healthCheckTimeout time.Duration ,
102
+ runFunc func (ctx context.Context )) * leaderElection {
75
103
return & leaderElection {
76
104
runFunc : runFunc ,
77
105
lockName : lockName ,
78
106
resourceLock : resourcelock .LeasesResourceLock ,
107
+ healthCheck : leaderelection .NewLeaderHealthzAdaptor (healthCheckTimeout ),
79
108
leaseDuration : defaultLeaseDuration ,
80
109
renewDeadline : defaultRenewDeadline ,
81
110
retryPeriod : defaultRetryPeriod ,
@@ -84,11 +113,18 @@ func NewLeaderElectionWithLeases(clientset kubernetes.Interface, lockName string
84
113
}
85
114
86
115
// NewLeaderElectionWithEndpoints returns an implementation of leader election using Endpoints
87
- func NewLeaderElectionWithEndpoints (clientset kubernetes.Interface , lockName string , runFunc func (ctx context.Context )) * leaderElection {
116
+ // healthCheckTimeout determines the max duration beyond lease expiration
117
+ // allowed before reporting unhealthy.
118
+ func NewLeaderElectionWithEndpoints (
119
+ clientset kubernetes.Interface ,
120
+ lockName string ,
121
+ healthCheckTimeout time.Duration ,
122
+ runFunc func (ctx context.Context )) * leaderElection {
88
123
return & leaderElection {
89
124
runFunc : runFunc ,
90
125
lockName : lockName ,
91
126
resourceLock : resourcelock .EndpointsResourceLock ,
127
+ healthCheck : leaderelection .NewLeaderHealthzAdaptor (healthCheckTimeout ),
92
128
leaseDuration : defaultLeaseDuration ,
93
129
renewDeadline : defaultRenewDeadline ,
94
130
retryPeriod : defaultRetryPeriod ,
@@ -97,11 +133,18 @@ func NewLeaderElectionWithEndpoints(clientset kubernetes.Interface, lockName str
97
133
}
98
134
99
135
// NewLeaderElectionWithConfigMaps returns an implementation of leader election using ConfigMaps
100
- func NewLeaderElectionWithConfigMaps (clientset kubernetes.Interface , lockName string , runFunc func (ctx context.Context )) * leaderElection {
136
+ // healthCheckTimeout determines the max duration beyond lease expiration
137
+ // allowed before reporting unhealthy.
138
+ func NewLeaderElectionWithConfigMaps (
139
+ clientset kubernetes.Interface ,
140
+ lockName string ,
141
+ healthCheckTimeout time.Duration ,
142
+ runFunc func (ctx context.Context )) * leaderElection {
101
143
return & leaderElection {
102
144
runFunc : runFunc ,
103
145
lockName : lockName ,
104
146
resourceLock : resourcelock .ConfigMapsResourceLock ,
147
+ healthCheck : leaderelection .NewLeaderHealthzAdaptor (healthCheckTimeout ),
105
148
leaseDuration : defaultLeaseDuration ,
106
149
renewDeadline : defaultRenewDeadline ,
107
150
retryPeriod : defaultRetryPeriod ,
@@ -134,6 +177,21 @@ func (l *leaderElection) WithContext(ctx context.Context) {
134
177
l .ctx = ctx
135
178
}
136
179
180
+ // Server represents any type that could serve HTTP requests for the leader
181
+ // election health check endpoint.
182
+ type Server interface {
183
+ Handle (pattern string , handler http.Handler )
184
+ }
185
+
186
+ // RegisterHealthCheck creates a health check for this leader election object
187
+ // and registers its HTTP handler to the given server at the path specified by
188
+ // the constant "healthCheckerAddress".
189
+ // The caller sidecar should document the handler address in appropriate flag
190
+ // descriptions.
191
+ func (l * leaderElection ) RegisterHealthCheck (s Server ) {
192
+ s .Handle (HealthCheckerAddress , adaptCheckToHandler (l .healthCheck .Check ))
193
+ }
194
+
137
195
func (l * leaderElection ) Run () error {
138
196
if l .identity == "" {
139
197
id , err := defaultLeaderElectionIdentity ()
@@ -179,6 +237,7 @@ func (l *leaderElection) Run() error {
179
237
klog .V (3 ).Infof ("new leader detected, current leader: %s" , identity )
180
238
},
181
239
},
240
+ WatchDog : l .healthCheck ,
182
241
}
183
242
184
243
ctx := l .ctx
@@ -220,3 +279,15 @@ func inClusterNamespace() string {
220
279
221
280
return "default"
222
281
}
282
+
283
+ // adaptCheckToHandler returns an http.HandlerFunc that serves the provided checks.
284
+ func adaptCheckToHandler (c func (r * http.Request ) error ) http.HandlerFunc {
285
+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
286
+ err := c (r )
287
+ if err != nil {
288
+ http .Error (w , fmt .Sprintf ("internal server error: %v" , err ), http .StatusInternalServerError )
289
+ } else {
290
+ fmt .Fprint (w , "ok" )
291
+ }
292
+ })
293
+ }
0 commit comments