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>
This commit is contained in:
Brian Goff 2017-04-08 14:43:42 -04:00
parent 458f6712d4
commit 408c7ade70
5 changed files with 76 additions and 65 deletions

View file

@ -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}")
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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