Browse code

Enable pprof/debug endpoints by default

Makes sure that debug endpoints are always available, which will aid in
debugging demon issues.

Wraps debug endpoints in the middleware chain so the can be blocked by
authz.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2017/04/09 03:43:42
Showing 5 changed files
1 1
deleted file mode 100644
... ...
@@ -1,46 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"expvar"
5
-	"fmt"
6
-	"net/http"
7
-	"net/http/pprof"
8
-
9
-	"github.com/gorilla/mux"
10
-)
11
-
12
-const debugPathPrefix = "/debug/"
13
-
14
-func profilerSetup(mainRouter *mux.Router) {
15
-	var r = mainRouter.PathPrefix(debugPathPrefix).Subrouter()
16
-	r.HandleFunc("/vars", expVars)
17
-	r.HandleFunc("/pprof/", pprof.Index)
18
-	r.HandleFunc("/pprof/cmdline", pprof.Cmdline)
19
-	r.HandleFunc("/pprof/profile", pprof.Profile)
20
-	r.HandleFunc("/pprof/symbol", pprof.Symbol)
21
-	r.HandleFunc("/pprof/trace", pprof.Trace)
22
-	r.HandleFunc("/pprof/{name}", handlePprof)
23
-}
24
-
25
-func handlePprof(w http.ResponseWriter, r *http.Request) {
26
-	var name string
27
-	if vars := mux.Vars(r); vars != nil {
28
-		name = vars["name"]
29
-	}
30
-	pprof.Handler(name).ServeHTTP(w, r)
31
-}
32
-
33
-// Replicated from expvar.go as not public.
34
-func expVars(w http.ResponseWriter, r *http.Request) {
35
-	first := true
36
-	w.Header().Set("Content-Type", "application/json; charset=utf-8")
37
-	fmt.Fprintln(w, "{")
38
-	expvar.Do(func(kv expvar.KeyValue) {
39
-		if !first {
40
-			fmt.Fprintln(w, ",")
41
-		}
42
-		first = false
43
-		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
44
-	})
45
-	fmt.Fprintln(w, "\n}")
46
-}
47 1
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+package debug
1
+
2
+import (
3
+	"expvar"
4
+	"net/http"
5
+	"net/http/pprof"
6
+
7
+	"github.com/docker/docker/api/server/httputils"
8
+	"github.com/docker/docker/api/server/router"
9
+	"golang.org/x/net/context"
10
+)
11
+
12
+// NewRouter creates a new debug router
13
+// The debug router holds endpoints for debug the daemon, such as those for pprof.
14
+func NewRouter() router.Router {
15
+	r := &debugRouter{}
16
+	r.initRoutes()
17
+	return r
18
+}
19
+
20
+type debugRouter struct {
21
+	routes []router.Route
22
+}
23
+
24
+func (r *debugRouter) initRoutes() {
25
+	r.routes = []router.Route{
26
+		router.NewGetRoute("/vars", frameworkAdaptHandler(expvar.Handler())),
27
+		router.NewGetRoute("/pprof/", frameworkAdaptHandlerFunc(pprof.Index)),
28
+		router.NewGetRoute("/pprof/cmdline", frameworkAdaptHandlerFunc(pprof.Cmdline)),
29
+		router.NewGetRoute("/pprof/profile", frameworkAdaptHandlerFunc(pprof.Profile)),
30
+		router.NewGetRoute("/pprof/symbol", frameworkAdaptHandlerFunc(pprof.Symbol)),
31
+		router.NewGetRoute("/pprof/trace", frameworkAdaptHandlerFunc(pprof.Trace)),
32
+		router.NewGetRoute("/pprof/{name}", handlePprof),
33
+	}
34
+}
35
+
36
+func (r *debugRouter) Routes() []router.Route {
37
+	return r.routes
38
+}
39
+
40
+func frameworkAdaptHandler(handler http.Handler) httputils.APIFunc {
41
+	return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
42
+		handler.ServeHTTP(w, r)
43
+		return nil
44
+	}
45
+}
46
+
47
+func frameworkAdaptHandlerFunc(handler http.HandlerFunc) httputils.APIFunc {
48
+	return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
49
+		handler(w, r)
50
+		return nil
51
+	}
52
+}
0 53
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package debug
1
+
2
+import (
3
+	"net/http"
4
+	"net/http/pprof"
5
+
6
+	"golang.org/x/net/context"
7
+)
8
+
9
+func handlePprof(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
10
+	pprof.Handler(vars["name"]).ServeHTTP(w, r)
11
+	return nil
12
+}
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/docker/docker/api/server/httputils"
13 13
 	"github.com/docker/docker/api/server/middleware"
14 14
 	"github.com/docker/docker/api/server/router"
15
+	"github.com/docker/docker/api/server/router/debug"
15 16
 	"github.com/docker/docker/dockerversion"
16 17
 	"github.com/gorilla/mux"
17 18
 	"golang.org/x/net/context"
... ...
@@ -148,13 +149,10 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
148 148
 
149 149
 // InitRouter initializes the list of routers for the server.
150 150
 // This method also enables the Go profiler if enableProfiler is true.
151
-func (s *Server) InitRouter(enableProfiler bool, routers ...router.Router) {
151
+func (s *Server) InitRouter(routers ...router.Router) {
152 152
 	s.routers = append(s.routers, routers...)
153 153
 
154 154
 	m := s.createMux()
155
-	if enableProfiler {
156
-		profilerSetup(m)
157
-	}
158 155
 	s.routerSwapper = &routerSwapper{
159 156
 		router: m,
160 157
 	}
... ...
@@ -175,6 +173,13 @@ func (s *Server) createMux() *mux.Router {
175 175
 		}
176 176
 	}
177 177
 
178
+	debugRouter := debug.NewRouter()
179
+	s.routers = append(s.routers, debugRouter)
180
+	for _, r := range debugRouter.Routes() {
181
+		f := s.makeHTTPHandler(r.Handler())
182
+		m.Path("/debug" + r.Path()).Handler(f)
183
+	}
184
+
178 185
 	err := errors.NewRequestNotFoundError(fmt.Errorf("page not found"))
179 186
 	notFoundHandler := httputils.MakeErrorHandler(err)
180 187
 	m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler)
... ...
@@ -194,15 +199,3 @@ func (s *Server) Wait(waitChan chan error) {
194 194
 	}
195 195
 	waitChan <- nil
196 196
 }
197
-
198
-// DisableProfiler reloads the server mux without adding the profiler routes.
199
-func (s *Server) DisableProfiler() {
200
-	s.routerSwapper.Swap(s.createMux())
201
-}
202
-
203
-// EnableProfiler reloads the server mux adding the profiler routes.
204
-func (s *Server) EnableProfiler() {
205
-	m := s.createMux()
206
-	profilerSetup(m)
207
-	s.routerSwapper.Swap(m)
208
-}
... ...
@@ -383,10 +383,8 @@ func (cli *DaemonCli) reloadConfig() {
383 383
 			switch {
384 384
 			case debugEnabled && !config.Debug: // disable debug
385 385
 				debug.Disable()
386
-				cli.api.DisableProfiler()
387 386
 			case config.Debug && !debugEnabled: // enable debug
388 387
 				debug.Enable()
389
-				cli.api.EnableProfiler()
390 388
 			}
391 389
 
392 390
 		}
... ...
@@ -536,7 +534,7 @@ func initRouter(opts routerOptions) {
536 536
 		}
537 537
 	}
538 538
 
539
-	opts.api.InitRouter(debug.IsEnabled(), routers...)
539
+	opts.api.InitRouter(routers...)
540 540
 }
541 541
 
542 542
 // TODO: remove this from cli and return the authzMiddleware