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:
parent
458f6712d4
commit
408c7ade70
5 changed files with 76 additions and 65 deletions
|
@ -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}")
|
||||
}
|
53
api/server/router/debug/debug.go
Normal file
53
api/server/router/debug/debug.go
Normal 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
|
||||
}
|
||||
}
|
13
api/server/router/debug/debug_routes.go
Normal file
13
api/server/router/debug/debug_routes.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue