From 408c7ade7008ca9b2181e12e51a01250a7a94413 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Sat, 8 Apr 2017 14:43:42 -0400 Subject: [PATCH] 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 --- api/server/profiler.go | 46 --------------------- api/server/router/debug/debug.go | 53 +++++++++++++++++++++++++ api/server/router/debug/debug_routes.go | 13 ++++++ api/server/server.go | 25 +++++------- cmd/dockerd/daemon.go | 4 +- 5 files changed, 76 insertions(+), 65 deletions(-) delete mode 100644 api/server/profiler.go create mode 100644 api/server/router/debug/debug.go create mode 100644 api/server/router/debug/debug_routes.go diff --git a/api/server/profiler.go b/api/server/profiler.go deleted file mode 100644 index d49be338c8..0000000000 --- a/api/server/profiler.go +++ /dev/null @@ -1,46 +0,0 @@ -package server - -import ( - "expvar" - "fmt" - "net/http" - "net/http/pprof" - - "github.com/gorilla/mux" -) - -const debugPathPrefix = "/debug/" - -func profilerSetup(mainRouter *mux.Router) { - var r = mainRouter.PathPrefix(debugPathPrefix).Subrouter() - r.HandleFunc("/vars", expVars) - r.HandleFunc("/pprof/", pprof.Index) - r.HandleFunc("/pprof/cmdline", pprof.Cmdline) - r.HandleFunc("/pprof/profile", pprof.Profile) - r.HandleFunc("/pprof/symbol", pprof.Symbol) - r.HandleFunc("/pprof/trace", pprof.Trace) - r.HandleFunc("/pprof/{name}", handlePprof) -} - -func handlePprof(w http.ResponseWriter, r *http.Request) { - var name string - if vars := mux.Vars(r); vars != nil { - name = vars["name"] - } - pprof.Handler(name).ServeHTTP(w, r) -} - -// Replicated from expvar.go as not public. -func expVars(w http.ResponseWriter, r *http.Request) { - first := true - w.Header().Set("Content-Type", "application/json; charset=utf-8") - fmt.Fprintln(w, "{") - expvar.Do(func(kv expvar.KeyValue) { - if !first { - fmt.Fprintln(w, ",") - } - first = false - fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) - }) - fmt.Fprintln(w, "\n}") -} diff --git a/api/server/router/debug/debug.go b/api/server/router/debug/debug.go new file mode 100644 index 0000000000..b66ff3cf3a --- /dev/null +++ b/api/server/router/debug/debug.go @@ -0,0 +1,53 @@ +package debug + +import ( + "expvar" + "net/http" + "net/http/pprof" + + "github.com/docker/docker/api/server/httputils" + "github.com/docker/docker/api/server/router" + "golang.org/x/net/context" +) + +// NewRouter creates a new debug router +// The debug router holds endpoints for debug the daemon, such as those for pprof. +func NewRouter() router.Router { + r := &debugRouter{} + r.initRoutes() + return r +} + +type debugRouter struct { + routes []router.Route +} + +func (r *debugRouter) initRoutes() { + r.routes = []router.Route{ + router.NewGetRoute("/vars", frameworkAdaptHandler(expvar.Handler())), + router.NewGetRoute("/pprof/", frameworkAdaptHandlerFunc(pprof.Index)), + router.NewGetRoute("/pprof/cmdline", frameworkAdaptHandlerFunc(pprof.Cmdline)), + router.NewGetRoute("/pprof/profile", frameworkAdaptHandlerFunc(pprof.Profile)), + router.NewGetRoute("/pprof/symbol", frameworkAdaptHandlerFunc(pprof.Symbol)), + router.NewGetRoute("/pprof/trace", frameworkAdaptHandlerFunc(pprof.Trace)), + router.NewGetRoute("/pprof/{name}", handlePprof), + } +} + +func (r *debugRouter) Routes() []router.Route { + return r.routes +} + +func frameworkAdaptHandler(handler http.Handler) httputils.APIFunc { + return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + handler.ServeHTTP(w, r) + return nil + } +} + +func frameworkAdaptHandlerFunc(handler http.HandlerFunc) httputils.APIFunc { + return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + handler(w, r) + return nil + } +} diff --git a/api/server/router/debug/debug_routes.go b/api/server/router/debug/debug_routes.go new file mode 100644 index 0000000000..f2a72615a0 --- /dev/null +++ b/api/server/router/debug/debug_routes.go @@ -0,0 +1,13 @@ +package debug + +import ( + "net/http" + "net/http/pprof" + + "golang.org/x/net/context" +) + +func handlePprof(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + pprof.Handler(vars["name"]).ServeHTTP(w, r) + return nil +} diff --git a/api/server/server.go b/api/server/server.go index d402019113..e0f2d89d9a 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -12,6 +12,7 @@ import ( "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/server/middleware" "github.com/docker/docker/api/server/router" + "github.com/docker/docker/api/server/router/debug" "github.com/docker/docker/dockerversion" "github.com/gorilla/mux" "golang.org/x/net/context" @@ -148,13 +149,10 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { // InitRouter initializes the list of routers for the server. // This method also enables the Go profiler if enableProfiler is true. -func (s *Server) InitRouter(enableProfiler bool, routers ...router.Router) { +func (s *Server) InitRouter(routers ...router.Router) { s.routers = append(s.routers, routers...) m := s.createMux() - if enableProfiler { - profilerSetup(m) - } s.routerSwapper = &routerSwapper{ router: m, } @@ -175,6 +173,13 @@ func (s *Server) createMux() *mux.Router { } } + debugRouter := debug.NewRouter() + s.routers = append(s.routers, debugRouter) + for _, r := range debugRouter.Routes() { + f := s.makeHTTPHandler(r.Handler()) + m.Path("/debug" + r.Path()).Handler(f) + } + err := errors.NewRequestNotFoundError(fmt.Errorf("page not found")) notFoundHandler := httputils.MakeErrorHandler(err) m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler) @@ -194,15 +199,3 @@ func (s *Server) Wait(waitChan chan error) { } waitChan <- nil } - -// DisableProfiler reloads the server mux without adding the profiler routes. -func (s *Server) DisableProfiler() { - s.routerSwapper.Swap(s.createMux()) -} - -// EnableProfiler reloads the server mux adding the profiler routes. -func (s *Server) EnableProfiler() { - m := s.createMux() - profilerSetup(m) - s.routerSwapper.Swap(m) -} diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index ad937ede7c..215f9c2d59 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -383,10 +383,8 @@ func (cli *DaemonCli) reloadConfig() { switch { case debugEnabled && !config.Debug: // disable debug debug.Disable() - cli.api.DisableProfiler() case config.Debug && !debugEnabled: // enable debug debug.Enable() - cli.api.EnableProfiler() } } @@ -536,7 +534,7 @@ func initRouter(opts routerOptions) { } } - opts.api.InitRouter(debug.IsEnabled(), routers...) + opts.api.InitRouter(routers...) } // TODO: remove this from cli and return the authzMiddleware