@@ -50,6 +50,7 @@ const (
50
50
51
51
defaultReadinessEndpoint = "/readyz"
52
52
defaultLivenessEndpoint = "/healthz"
53
+ defaultMetricsEndpoint = "/metrics"
53
54
)
54
55
55
56
var log = logf .RuntimeLog .WithName ("manager" )
@@ -95,6 +96,9 @@ type controllerManager struct {
95
96
// metricsListener is used to serve prometheus metrics
96
97
metricsListener net.Listener
97
98
99
+ // metricsExtraHandlers contains extra handlers to register on http server that serves metrics.
100
+ metricsExtraHandlers map [string ]http.Handler
101
+
98
102
// healthProbeListener is used to serve liveness probe
99
103
healthProbeListener net.Listener
100
104
@@ -260,6 +264,25 @@ func (cm *controllerManager) SetFields(i interface{}) error {
260
264
return nil
261
265
}
262
266
267
+ // AddMetricsExtraHandler adds extra handler served on path to the http server that serves metrics.
268
+ func (cm * controllerManager ) AddMetricsExtraHandler (path string , handler http.Handler ) error {
269
+ if path == defaultMetricsEndpoint {
270
+ return fmt .Errorf ("overriding builtin %s endpoint is not allowed" , defaultMetricsEndpoint )
271
+ }
272
+
273
+ cm .mu .Lock ()
274
+ defer cm .mu .Unlock ()
275
+
276
+ _ , found := cm .metricsExtraHandlers [path ]
277
+ if found {
278
+ return fmt .Errorf ("can't register extra handler by duplicate path %q on metrics http server" , path )
279
+ }
280
+
281
+ cm .metricsExtraHandlers [path ] = handler
282
+ log .V (2 ).Info ("Registering metrics http server extra handler" , "path" , path )
283
+ return nil
284
+ }
285
+
263
286
// AddHealthzCheck allows you to add Healthz checker
264
287
func (cm * controllerManager ) AddHealthzCheck (name string , check healthz.Checker ) error {
265
288
cm .mu .Lock ()
@@ -341,19 +364,28 @@ func (cm *controllerManager) GetWebhookServer() *webhook.Server {
341
364
}
342
365
343
366
func (cm * controllerManager ) serveMetrics (stop <- chan struct {}) {
344
- var metricsPath = "/metrics"
345
367
handler := promhttp .HandlerFor (metrics .Registry , promhttp.HandlerOpts {
346
368
ErrorHandling : promhttp .HTTPErrorOnError ,
347
369
})
348
370
// TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics
349
371
mux := http .NewServeMux ()
350
- mux .Handle (metricsPath , handler )
372
+ mux .Handle (defaultMetricsEndpoint , handler )
373
+
374
+ func () {
375
+ cm .mu .Lock ()
376
+ defer cm .mu .Unlock ()
377
+
378
+ for path , extraHandler := range cm .metricsExtraHandlers {
379
+ mux .Handle (path , extraHandler )
380
+ }
381
+ }()
382
+
351
383
server := http.Server {
352
384
Handler : mux ,
353
385
}
354
386
// Run the server
355
387
go func () {
356
- log .Info ("starting metrics server" , "path" , metricsPath )
388
+ log .Info ("starting metrics server" , "path" , defaultMetricsEndpoint )
357
389
if err := server .Serve (cm .metricsListener ); err != nil && err != http .ErrServerClosed {
358
390
cm .errSignal .SignalError (err )
359
391
}
@@ -367,6 +399,8 @@ func (cm *controllerManager) serveMetrics(stop <-chan struct{}) {
367
399
}
368
400
369
401
func (cm * controllerManager ) serveHealthProbes (stop <- chan struct {}) {
402
+ // TODO(hypnoglow): refactor locking to use anonymous func in the similar way
403
+ // it's done in serveMetrics.
370
404
cm .mu .Lock ()
371
405
mux := http .NewServeMux ()
372
406
0 commit comments