Skip to content

Commit d95ee5c

Browse files
committed
Expose metrics http server for extra endpoints
This allows users to register extra http endpoints on the http server that serves metrics.
1 parent f284dc3 commit d95ee5c

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

pkg/manager/internal.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ type controllerManager struct {
9595
// metricsListener is used to serve prometheus metrics
9696
metricsListener net.Listener
9797

98+
// metricsExtraHandlers contains extra handlers to register on http server that serves metrics.
99+
metricsExtraHandlers map[string]http.Handler
100+
98101
// healthProbeListener is used to serve liveness probe
99102
healthProbeListener net.Listener
100103

@@ -260,6 +263,21 @@ func (cm *controllerManager) SetFields(i interface{}) error {
260263
return nil
261264
}
262265

266+
// AddMetricsExtraHandler adds extra handler served on path to the http server that serves metrics.
267+
func (cm *controllerManager) AddMetricsExtraHandler(path string, handler http.Handler) error {
268+
cm.mu.Lock()
269+
defer cm.mu.Unlock()
270+
271+
_, found := cm.metricsExtraHandlers[path]
272+
if found {
273+
return fmt.Errorf("can't register extra handler by duplicate path %q on metrics http server", path)
274+
}
275+
276+
cm.metricsExtraHandlers[path] = handler
277+
log.V(2).Info("Registering metrics http server extra handler", "path", path)
278+
return nil
279+
}
280+
263281
// AddHealthzCheck allows you to add Healthz checker
264282
func (cm *controllerManager) AddHealthzCheck(name string, check healthz.Checker) error {
265283
cm.mu.Lock()
@@ -348,6 +366,16 @@ func (cm *controllerManager) serveMetrics(stop <-chan struct{}) {
348366
// TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics
349367
mux := http.NewServeMux()
350368
mux.Handle(metricsPath, handler)
369+
370+
func() {
371+
cm.mu.Lock()
372+
defer cm.mu.Unlock()
373+
374+
for path, extraHandler := range cm.metricsExtraHandlers {
375+
mux.Handle(path, extraHandler)
376+
}
377+
}()
378+
351379
server := http.Server{
352380
Handler: mux,
353381
}

pkg/manager/manager.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package manager
1919
import (
2020
"fmt"
2121
"net"
22+
"net/http"
2223
"time"
2324

2425
"github.com/go-logr/logr"
@@ -54,6 +55,13 @@ type Manager interface {
5455
// interface - e.g. inject.Client.
5556
SetFields(interface{}) error
5657

58+
// AddMetricsExtraHandler adds an extra handler served on path to the http server that serves metrics.
59+
// Might be useful to register some diagnostic endpoints e.g. pprof. Note that these endpoints meant to be
60+
// sensitive and shouldn't be exposed publicly.
61+
// If the simple path -> handler mapping offered here is not enough, a new http server/listener should be added as
62+
// Runnable to the manager via Add method.
63+
AddMetricsExtraHandler(path string, handler http.Handler) error
64+
5765
// AddHealthzCheck allows you to add Healthz checker
5866
AddHealthzCheck(name string, check healthz.Checker) error
5967

@@ -282,6 +290,9 @@ func New(config *rest.Config, options Options) (Manager, error) {
282290
return nil, err
283291
}
284292

293+
// By default we have no extra endpoints to expose on metrics http server.
294+
metricsExtraHandlers := make(map[string]http.Handler)
295+
285296
// Create health probes listener. This will throw an error if the bind
286297
// address is invalid or already in use.
287298
healthProbeListener, err := options.newHealthProbeListener(options.HealthProbeBindAddress)
@@ -302,6 +313,7 @@ func New(config *rest.Config, options Options) (Manager, error) {
302313
resourceLock: resourceLock,
303314
mapper: mapper,
304315
metricsListener: metricsListener,
316+
metricsExtraHandlers: metricsExtraHandlers,
305317
internalStop: stop,
306318
internalStopper: stop,
307319
port: options.Port,

pkg/manager/manager_test.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ var _ = Describe("manger.Manager", func() {
412412
Expect(resp.StatusCode).To(Equal(200))
413413
})
414414

415-
It("should not serve anything other than metrics endpoint", func(done Done) {
415+
It("should not serve anything other than metrics endpoint by default", func(done Done) {
416416
opts.MetricsBindAddress = ":0"
417417
m, err := New(cfg, opts)
418418
Expect(err).NotTo(HaveOccurred())
@@ -469,6 +469,40 @@ var _ = Describe("manger.Manager", func() {
469469
ok := metrics.Registry.Unregister(one)
470470
Expect(ok).To(BeTrue())
471471
})
472+
473+
It("should serve extra endpoints", func(done Done) {
474+
opts.MetricsBindAddress = ":0"
475+
m, err := New(cfg, opts)
476+
Expect(err).NotTo(HaveOccurred())
477+
478+
err = m.AddMetricsExtraHandler("/debug", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
479+
_, _ = w.Write([]byte("Some debug info"))
480+
}))
481+
Expect(err).NotTo(HaveOccurred())
482+
483+
// Should error when we add another extra endpoint on the already registered path.
484+
err = m.AddMetricsExtraHandler("/debug", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
485+
_, _ = w.Write([]byte("Another debug info"))
486+
}))
487+
Expect(err).To(HaveOccurred())
488+
489+
s := make(chan struct{})
490+
defer close(s)
491+
go func() {
492+
defer GinkgoRecover()
493+
Expect(m.Start(s)).NotTo(HaveOccurred())
494+
close(done)
495+
}()
496+
497+
endpoint := fmt.Sprintf("http://%s/debug", listener.Addr().String())
498+
resp, err := http.Get(endpoint)
499+
Expect(err).NotTo(HaveOccurred())
500+
Expect(resp.StatusCode).To(Equal(http.StatusOK))
501+
502+
body, err := ioutil.ReadAll(resp.Body)
503+
Expect(err).NotTo(HaveOccurred())
504+
Expect(string(body)).To(Equal("Some debug info"))
505+
})
472506
})
473507
})
474508

0 commit comments

Comments
 (0)