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