1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package debugstatus
import (
"net/http"
"golang.org/x/net/trace"
"gopkg.in/errgo.v1"
pprof "github.com/juju/httpprof"
"golang.org/x/net/context"
"gopkg.in/httprequest.v1"
)
// Version describes the current version of the code being run.
type Version struct {
GitCommit string
Version string
}
// Handler implements a type that can be used with httprequest.Handlers
// to serve a standard set of /debug endpoints, including
// the version of the system, its current health status
// the runtime profiling information.
type Handler struct {
// Check will be called to obtain the current health of the
// system. It should return a map as returned from the
// Check function. If this is nil, an empty result will
// always be returned from /debug/status.
Check func(context.Context) map[string]CheckResult
// Version should hold the current version
// of the binary running the server, served
// from the /debug/info endpoint.
Version Version
// CheckPprofAllowed will be used to check whether the
// given pprof request should be allowed.
// It should return an error if not, which will not be masked.
// If this is nil, no access will be allowed to any
// of the endpoints under /debug/pprof - the
// error returned will be ErrNoPprofConfigured.
CheckPprofAllowed func(req *http.Request) error
// CheckTraceAllowed will be used to check whether the given
// trace request should be allowed. It should return an error if
// not, which will not be masked. If this is nil, no access will
// be allowed to either /debug/events or /debug/requests - the
// error returned will be ErrNoTraceConfigured. If access is
// allowed, the sensitive value specifies whether sensitive trace
// events will be shown.
CheckTraceAllowed func(req *http.Request) (sensitive bool, err error)
}
// DebugStatusRequest describes the /debug/status endpoint.
type DebugStatusRequest struct {
httprequest.Route `httprequest:"GET /debug/status"`
}
// DebugStatus returns the current status of the server.
func (h *Handler) DebugStatus(p httprequest.Params, _ *DebugStatusRequest) (map[string]CheckResult, error) {
if h.Check == nil {
return map[string]CheckResult{}, nil
}
return h.Check(p.Context), nil
}
// DebugInfoRequest describes the /debug/info endpoint.
type DebugInfoRequest struct {
httprequest.Route `httprequest:"GET /debug/info"`
}
// DebugInfo returns version information on the current server.
func (h *Handler) DebugInfo(*DebugInfoRequest) (Version, error) {
return h.Version, nil
}
// DebugPprofRequest describes the /debug/pprof/ endpoint.
type DebugPprofRequest struct {
httprequest.Route `httprequest:"GET /debug/pprof/"`
}
// DebugPprof serves index information on the available pprof endpoints.
func (h *Handler) DebugPprof(p httprequest.Params, _ *DebugPprofRequest) error {
if err := h.checkPprofAllowed(p.Request); err != nil {
return err
}
pprof.Index(p.Response, p.Request)
return nil
}
// DebugPprofEndpointsRequest describes the endpoints under /debug/prof.
type DebugPprofEndpointsRequest struct {
httprequest.Route `httprequest:"GET /debug/pprof/:name"`
Name string `httprequest:"name,path"`
}
// DebugPprofEndpoints serves all the endpoints under DebugPprof.
func (h *Handler) DebugPprofEndpoints(p httprequest.Params, r *DebugPprofEndpointsRequest) error {
if err := h.checkPprofAllowed(p.Request); err != nil {
return err
}
switch r.Name {
case "cmdline":
pprof.Cmdline(p.Response, p.Request)
case "profile":
pprof.Profile(p.Response, p.Request)
case "symbol":
pprof.Symbol(p.Response, p.Request)
default:
pprof.Handler(r.Name).ServeHTTP(p.Response, p.Request)
}
return nil
}
// ErrNoPprofConfigured is the error returned on access
// to endpoints when Handler.CheckPprofAllowed is nil.
var ErrNoPprofConfigured = errgo.New("no pprof access configured")
// checkPprofAllowed is used instead of h.CheckPprofAllowed
// so that we don't panic if that is nil.
func (h *Handler) checkPprofAllowed(req *http.Request) error {
if h.CheckPprofAllowed == nil {
return ErrNoPprofConfigured
}
return h.CheckPprofAllowed(req)
}
// DebugEventsRequest describes the /debug/events endpoint.
type DebugEventsRequest struct {
httprequest.Route `httprequest:"GET /debug/events"`
}
// DebugEvents serves the /debug/events endpoint.
func (h *Handler) DebugEvents(p httprequest.Params, r *DebugEventsRequest) error {
sensitive, err := h.checkTraceAllowed(p.Request)
if err != nil {
return errgo.Mask(err, errgo.Any)
}
trace.RenderEvents(p.Response, p.Request, sensitive)
return nil
}
// DebugRequestsRequest describes the /debug/requests endpoint.
type DebugRequestsRequest struct {
httprequest.Route `httprequest:"GET /debug/requests"`
}
// DebugRequests serves the /debug/requests endpoint.
func (h *Handler) DebugRequests(p httprequest.Params, r *DebugRequestsRequest) error {
sensitive, err := h.checkTraceAllowed(p.Request)
if err != nil {
return errgo.Mask(err, errgo.Any)
}
trace.Render(p.Response, p.Request, sensitive)
return nil
}
// ErrNoTraceConfigured is the error returned on access
// to endpoints when Handler.CheckTraceAllowed is nil.
var ErrNoTraceConfigured = errgo.New("no trace access configured")
// checkTraceAllowed is used instead of h.CheckTraceAllowed
// so that we don't panic if that is nil.
func (h *Handler) checkTraceAllowed(req *http.Request) (bool, error) {
if h.CheckTraceAllowed == nil {
return false, ErrNoTraceConfigured
}
return h.CheckTraceAllowed(req)
}
|