Skip to content

Commit

Permalink
refactor api profile methods
Browse files Browse the repository at this point in the history
comment why we ignore errors parsing params
  • Loading branch information
drewbailey committed Jan 9, 2020
1 parent ad86438 commit a58b8a5
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 131 deletions.
110 changes: 39 additions & 71 deletions api/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,106 +290,74 @@ func (a *Agent) Monitor(stopCh <-chan struct{}, q *QueryOptions) (<-chan *Stream
return frames, errCh
}

// PprofOptions contain a set of parameters for profiling a node or server.
type PprofOptions struct {
// ServerID is the server ID, name, or special value "leader" to
// specify the server that a given profile should be run on.
ServerID string

// NodeID is the node ID that a given profile should be run on.
NodeID string

// Seconds specifies the amount of time a profile should be run for.
// Seconds only applies for certain runtime profiles like CPU and Trace.
Seconds int

// GC determines if a runtime.GC() should be called before a heap
// profile.
GC int

// Debug specifies if the output of a lookup profile should be returned
// in human readable format instead of binary.
Debug int
}

// CPUProfile returns a runtime/pprof cpu profile for a given server or node.
// The profile will run for the amount of seconds passed in or default to 1.
// If no serverID or nodeID are provided the current Agents server will be
// used.
//
// The parameters are:
// * serverID: server ID or name to query, also accepts "leader"
// * nodeID: client node ID to query
// * seconds: the amount of time to run the trace for.
//
// The call blocks until the profile finishes, and returns the raw bytes of the
// profile.
func (a *Agent) CPUProfile(serverID, nodeID string, seconds int, q *QueryOptions) ([]byte, error) {
if q == nil {
q = &QueryOptions{}
}
if q.Params == nil {
q.Params = make(map[string]string)
}

q.Params["seconds"] = strconv.Itoa(seconds)
q.Params["node_id"] = nodeID
q.Params["server_id"] = serverID

body, err := a.client.rawQuery("/v1/agent/pprof/profile", q)
if err != nil {
return nil, err
}

resp, err := ioutil.ReadAll(body)
if err != nil {
return nil, err
}

return resp, nil
func (a *Agent) CPUProfile(opts PprofOptions, q *QueryOptions) ([]byte, error) {
return a.pprofRequest("profile", opts, q)
}

// Trace returns a runtime/pprof trace for a given server or node.
// The trace will run for the amount of seconds passed in or default to 1.
// If no serverID or nodeID are provided the current Agents server will be
// used.
//
// The parameters are:
// * serverID: server ID or name to query, also accepts "leader"
// * nodeID: client node ID to query
// * seconds: the amount of time to run the trace for.
//
// The call blocks until the profile finishes, and returns the raw bytes of the
// profile.
func (a *Agent) Trace(serverID, nodeID string, seconds int, q *QueryOptions) ([]byte, error) {
if q == nil {
q = &QueryOptions{}
}
if q.Params == nil {
q.Params = make(map[string]string)
}

q.Params["seconds"] = strconv.Itoa(seconds)
q.Params["node_id"] = nodeID
q.Params["server_id"] = serverID

body, err := a.client.rawQuery("/v1/agent/pprof/trace", q)
if err != nil {
return nil, err
}

resp, err := ioutil.ReadAll(body)
if err != nil {
return nil, err
}

return resp, nil

func (a *Agent) Trace(opts PprofOptions, q *QueryOptions) ([]byte, error) {
return a.pprofRequest("trace", opts, q)
}

// Profile returns a runtime/pprof profile using pprof.Lookup to determine
// Lookup returns a runtime/pprof profile using pprof.Lookup to determine
// which profile to run. Accepts a client or server ID but not both simultaneously.
//
// The parameters are:
// * serverID: server ID or name to query, also accepts "leader"
// * nodeID: client node ID to query
// * profile: the name of the runtime/pprof profile to lookup and run.
// * debug: flag to specify if the profile should return human readable output.
//
// The call blocks until the profile finishes, and returns the raw bytes of the
// profile.
func (a *Agent) Profile(serverID, nodeID, profile string, debug, gc int, q *QueryOptions) ([]byte, error) {
// profile unless debug is set.
func (a *Agent) Lookup(profile string, opts PprofOptions, q *QueryOptions) ([]byte, error) {
return a.pprofRequest(profile, opts, q)
}

func (a *Agent) pprofRequest(req string, opts PprofOptions, q *QueryOptions) ([]byte, error) {
if q == nil {
q = &QueryOptions{}
}
if q.Params == nil {
q.Params = make(map[string]string)
}

q.Params["debug"] = strconv.Itoa(debug)
q.Params["qc"] = strconv.Itoa(debug)
q.Params["node_id"] = nodeID
q.Params["server_id"] = serverID
q.Params["seconds"] = strconv.Itoa(opts.Seconds)
q.Params["debug"] = strconv.Itoa(opts.Debug)
q.Params["gc"] = strconv.Itoa(opts.GC)
q.Params["node_id"] = opts.NodeID
q.Params["server_id"] = opts.ServerID

body, err := a.client.rawQuery(fmt.Sprintf("/v1/agent/pprof/%s", profile), q)
body, err := a.client.rawQuery(fmt.Sprintf("/v1/agent/pprof/%s", req), q)
if err != nil {
return nil, err
}
Expand Down
17 changes: 12 additions & 5 deletions api/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,14 +391,21 @@ func TestAgentCPUProfile(t *testing.T) {

// Valid local request
{
resp, err := agent.CPUProfile("", "", 1, q)
opts := PprofOptions{
Seconds: 1,
}
resp, err := agent.CPUProfile(opts, q)
require.NoError(t, err)
require.NotNil(t, resp)
}

// Invalid server request
{
resp, err := agent.CPUProfile("unknown.global", "", 1, q)
opts := PprofOptions{
Seconds: 1,
ServerID: "unknown.global",
}
resp, err := agent.CPUProfile(opts, q)
require.Error(t, err)
require.Contains(t, err.Error(), "500 (unknown nomad server unknown.global)")
require.Nil(t, resp)
Expand All @@ -418,7 +425,7 @@ func TestAgentTrace(t *testing.T) {
AuthToken: token.SecretID,
}

resp, err := agent.Trace("", "", 1, q)
resp, err := agent.Trace(PprofOptions{}, q)
require.NoError(t, err)
require.NotNil(t, resp)
}
Expand All @@ -436,14 +443,14 @@ func TestAgentProfile(t *testing.T) {
}

{
resp, err := agent.Profile("", "", "heap", 0, 1, q)
resp, err := agent.Lookup("heap", PprofOptions{}, q)
require.NoError(t, err)
require.NotNil(t, resp)
}

// unknown profile
{
resp, err := agent.Profile("", "", "invalid", 1, 1, q)
resp, err := agent.Lookup("invalid", PprofOptions{}, q)
require.Error(t, err)
require.Contains(t, err.Error(), "Unexpected response code: 404")
require.Nil(t, resp)
Expand Down
2 changes: 2 additions & 0 deletions command/agent/agent_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ func (s *HTTPServer) AgentPprofRequest(resp http.ResponseWriter, req *http.Reque
func (s *HTTPServer) agentPprof(reqType pprof.ReqType, resp http.ResponseWriter, req *http.Request) ([]byte, error) {

// Parse query param int values
// Errors are dropped here and default to their zero values.
// This is to mimick the functionality that net/pprof implements.
seconds, _ := strconv.Atoi(req.URL.Query().Get("seconds"))
debug, _ := strconv.Atoi(req.URL.Query().Get("debug"))
gc, _ := strconv.Atoi(req.URL.Query().Get("gc"))
Expand Down
110 changes: 55 additions & 55 deletions command/agent/bindata_assetfs.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ui/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12823,3 +12823,4 @@ yup@^0.26.10:
property-expr "^1.5.0"
synchronous-promise "^2.0.5"
toposort "^2.0.2"

0 comments on commit a58b8a5

Please sign in to comment.