Skip to content

Commit 88ca0c6

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 88ca0c6

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

pkg/manager/internal.go

Lines changed: 24 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,20 @@ 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) {
268+
cm.mu.Lock()
269+
defer cm.mu.Unlock()
270+
271+
_, found := cm.metricsExtraHandlers[path]
272+
if found {
273+
panic(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.Info("registering metrics http server extra handler", "path", path)
278+
}
279+
263280
// AddHealthzCheck allows you to add Healthz checker
264281
func (cm *controllerManager) AddHealthzCheck(name string, check healthz.Checker) error {
265282
cm.mu.Lock()
@@ -348,6 +365,13 @@ func (cm *controllerManager) serveMetrics(stop <-chan struct{}) {
348365
// TODO(JoelSpeed): Use existing Kubernetes machinery for serving metrics
349366
mux := http.NewServeMux()
350367
mux.Handle(metricsPath, handler)
368+
369+
cm.mu.Lock()
370+
for path, extraHandler := range cm.metricsExtraHandlers {
371+
mux.Handle(path, extraHandler)
372+
}
373+
cm.mu.Unlock()
374+
351375
server := http.Server{
352376
Handler: mux,
353377
}

pkg/manager/manager.go

Lines changed: 9 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,10 @@ 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.
60+
AddMetricsExtraHandler(path string, handler http.Handler)
61+
5762
// AddHealthzCheck allows you to add Healthz checker
5863
AddHealthzCheck(name string, check healthz.Checker) error
5964

@@ -282,6 +287,9 @@ func New(config *rest.Config, options Options) (Manager, error) {
282287
return nil, err
283288
}
284289

290+
// By default we have no extra endpoints to expose on metrics http server.
291+
metricsExtraHandlers := make(map[string]http.Handler)
292+
285293
// Create health probes listener. This will throw an error if the bind
286294
// address is invalid or already in use.
287295
healthProbeListener, err := options.newHealthProbeListener(options.HealthProbeBindAddress)
@@ -302,6 +310,7 @@ func New(config *rest.Config, options Options) (Manager, error) {
302310
resourceLock: resourceLock,
303311
mapper: mapper,
304312
metricsListener: metricsListener,
313+
metricsExtraHandlers: metricsExtraHandlers,
305314
internalStop: stop,
306315
internalStopper: stop,
307316
port: options.Port,

pkg/manager/manager_test.go

Lines changed: 36 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,41 @@ 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+
m.AddMetricsExtraHandler("/debug", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
479+
_, _ = w.Write([]byte("Some debug info"))
480+
}))
481+
482+
// Should panic when we add another extra endpoint on the already registered path.
483+
registerDuplicateEndpoint := func() {
484+
m.AddMetricsExtraHandler("/debug", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
485+
_, _ = w.Write([]byte("Another debug info"))
486+
}))
487+
}
488+
Expect(registerDuplicateEndpoint).To(Panic())
489+
490+
s := make(chan struct{})
491+
defer close(s)
492+
go func() {
493+
defer GinkgoRecover()
494+
Expect(m.Start(s)).NotTo(HaveOccurred())
495+
close(done)
496+
}()
497+
498+
endpoint := fmt.Sprintf("http://%s/debug", listener.Addr().String())
499+
resp, err := http.Get(endpoint)
500+
Expect(err).NotTo(HaveOccurred())
501+
Expect(resp.StatusCode).To(Equal(http.StatusOK))
502+
503+
body, err := ioutil.ReadAll(resp.Body)
504+
Expect(err).NotTo(HaveOccurred())
505+
Expect(string(body)).To(Equal("Some debug info"))
506+
})
472507
})
473508
})
474509

0 commit comments

Comments
 (0)