@@ -50,6 +50,7 @@ const (
5050
5151	defaultReadinessEndpoint  =  "/readyz" 
5252	defaultLivenessEndpoint   =  "/healthz" 
53+ 	defaultMetricsEndpoint    =  "/metrics" 
5354)
5455
5556var  log  =  logf .RuntimeLog .WithName ("manager" )
@@ -95,6 +96,9 @@ type controllerManager struct {
9596	// metricsListener is used to serve prometheus metrics 
9697	metricsListener  net.Listener 
9798
99+ 	// metricsExtraHandlers contains extra handlers to register on http server that serves metrics. 
100+ 	metricsExtraHandlers  map [string ]http.Handler 
101+ 
98102	// healthProbeListener is used to serve liveness probe 
99103	healthProbeListener  net.Listener 
100104
@@ -260,6 +264,25 @@ func (cm *controllerManager) SetFields(i interface{}) error {
260264	return  nil 
261265}
262266
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+ 
263286// AddHealthzCheck allows you to add Healthz checker 
264287func  (cm  * controllerManager ) AddHealthzCheck (name  string , check  healthz.Checker ) error  {
265288	cm .mu .Lock ()
@@ -341,19 +364,28 @@ func (cm *controllerManager) GetWebhookServer() *webhook.Server {
341364}
342365
343366func  (cm  * controllerManager ) serveMetrics (stop  <- chan  struct {}) {
344- 	var  metricsPath  =  "/metrics" 
345367	handler  :=  promhttp .HandlerFor (metrics .Registry , promhttp.HandlerOpts {
346368		ErrorHandling : promhttp .HTTPErrorOnError ,
347369	})
348370	// TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics 
349371	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+ 
351383	server  :=  http.Server {
352384		Handler : mux ,
353385	}
354386	// Run the server 
355387	go  func () {
356- 		log .Info ("starting metrics server" , "path" , metricsPath )
388+ 		log .Info ("starting metrics server" , "path" , defaultMetricsEndpoint )
357389		if  err  :=  server .Serve (cm .metricsListener ); err  !=  nil  &&  err  !=  http .ErrServerClosed  {
358390			cm .errSignal .SignalError (err )
359391		}
@@ -367,6 +399,8 @@ func (cm *controllerManager) serveMetrics(stop <-chan struct{}) {
367399}
368400
369401func  (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. 
370404	cm .mu .Lock ()
371405	mux  :=  http .NewServeMux ()
372406
0 commit comments