open-vault/vault/external_tests/pprof/pprof_test.go
Calvin Leung Huang f4fd84d52b
sys: add pprof endpoint (#7473)
* sys/pprof: add pprof routes to the system backend

* sys/pprof: add pprof paths to handler with local-only check

* fix trailing slash on pprof index endpoint

* use new no-forward handler on pprof

* go mod tidy

* add pprof external tests

* disallow streaming requests to exceed DefaultMaxRequestDuration

* add max request duration test
2019-09-19 13:44:37 -07:00

174 lines
3.6 KiB
Go

package pprof
import (
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"strings"
"testing"
"github.com/hashicorp/go-cleanhttp"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/vault"
"golang.org/x/net/http2"
)
func TestSysPprof(t *testing.T) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client
transport := cleanhttp.DefaultPooledTransport()
transport.TLSClientConfig = cluster.Cores[0].TLSConfig.Clone()
if err := http2.ConfigureTransport(transport); err != nil {
t.Fatal(err)
}
httpClient := &http.Client{
Transport: transport,
}
cases := []struct {
name string
path string
seconds string
}{
{
"index",
"/v1/sys/pprof/",
"",
},
{
"cmdline",
"/v1/sys/pprof/cmdline",
"",
},
{
"goroutine",
"/v1/sys/pprof/goroutine",
"",
},
{
"heap",
"/v1/sys/pprof/heap",
"",
},
{
"profile",
"/v1/sys/pprof/profile",
"1",
},
{
"symbol",
"/v1/sys/pprof/symbol",
"",
},
{
"trace",
"/v1/sys/pprof/trace",
"1",
},
}
pprofRequest := func(path string, seconds string) {
req := client.NewRequest("GET", path)
if seconds != "" {
req.Params.Set("seconds", seconds)
}
httpReq, err := req.ToHTTP()
if err != nil {
t.Fatal(err)
}
resp, err := httpClient.Do(httpReq)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
httpRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
httpResp := make(map[string]interface{})
// Skip this error check since some endpoints return binary blobs, we
// only care about the ok check right after as an existence check.
_ = json.Unmarshal(httpRespBody, &httpResp)
// Make sure that we don't get a error response
if _, ok := httpResp["errors"]; ok {
t.Fatalf("unexpected error response: %v", httpResp["errors"])
}
if len(httpRespBody) == 0 {
t.Fatal("no pprof index returned")
}
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
pprofRequest(tc.path, tc.seconds)
})
}
}
func TestSysPprof_MaxRequestDuration(t *testing.T) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
client := cluster.Cores[0].Client
transport := cleanhttp.DefaultPooledTransport()
transport.TLSClientConfig = cluster.Cores[0].TLSConfig.Clone()
if err := http2.ConfigureTransport(transport); err != nil {
t.Fatal(err)
}
httpClient := &http.Client{
Transport: transport,
}
sec := strconv.Itoa(int(vault.DefaultMaxRequestDuration.Seconds()) + 1)
req := client.NewRequest("GET", "/v1/sys/pprof/profile")
req.Params.Set("seconds", sec)
httpReq, err := req.ToHTTP()
if err != nil {
t.Fatal(err)
}
resp, err := httpClient.Do(httpReq)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
httpRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
httpResp := make(map[string]interface{})
// If we error here, it means that profiling likely happened, which is not
// what we're checking for in this case.
if err := json.Unmarshal(httpRespBody, &httpResp); err != nil {
t.Fatalf("expected valid error response, got: %v", err)
}
errs, ok := httpResp["errors"].([]interface{})
if !ok {
t.Fatalf("expected error response, got: %v", httpResp)
}
if len(errs) == 0 || !strings.Contains(errs[0].(string), "exceeds max request duration") {
t.Fatalf("unexptected error returned: %v", errs)
}
}