cli: refactor operator debug capture (#11466)
* debug: refactor Consul API collection * debug: refactor Vault API collection * debug: cleanup test timing * debug: extend test to multiregion * debug: save cmdline flags in bundle * debug: add cli version to output * Add changelog entry
This commit is contained in:
parent
73e3b15305
commit
3c04d7927b
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
cli: Improve debug capture for Consul/Vault
|
||||
```
|
|
@ -24,6 +24,14 @@ func RequireConsul(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// RequireVault skips tests unless a Vault binary is available on $PATH.
|
||||
func RequireVault(t *testing.T) {
|
||||
_, err := exec.Command("vault", "version").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Skipf("Test requires Vault: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExecCompatible(t *testing.T) {
|
||||
if runtime.GOOS != "linux" || syscall.Geteuid() != 0 {
|
||||
t.Skip("Test only available running as root on linux")
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
|
@ -24,6 +25,7 @@ import (
|
|||
"github.com/hashicorp/nomad/api/contexts"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/version"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
|
@ -391,6 +393,9 @@ func (c *OperatorDebugCommand) Run(args []string) int {
|
|||
|
||||
c.collectDir = tmp
|
||||
|
||||
// Write CLI flags to JSON file
|
||||
c.writeFlags(flags)
|
||||
|
||||
// Create an instance of the API client
|
||||
client, err := c.Meta.Client()
|
||||
if err != nil {
|
||||
|
@ -496,6 +501,7 @@ func (c *OperatorDebugCommand) Run(args []string) int {
|
|||
// Display general info about the capture
|
||||
c.Ui.Output("Starting debugger...")
|
||||
c.Ui.Output("")
|
||||
c.Ui.Output(fmt.Sprintf("Nomad CLI Version: %s", version.GetVersion().FullVersionNumber(true)))
|
||||
c.Ui.Output(fmt.Sprintf(" Region: %s", c.region))
|
||||
c.Ui.Output(fmt.Sprintf(" Namespace: %s", c.namespace))
|
||||
c.Ui.Output(fmt.Sprintf(" Servers: (%d/%d) %v", serverCaptureCount, serversFound, c.serverIDs))
|
||||
|
@ -560,39 +566,19 @@ func (c *OperatorDebugCommand) collect(client *api.Client) error {
|
|||
regions, err := client.Regions().List()
|
||||
c.writeJSON(clusterDir, "regions.json", regions, err)
|
||||
|
||||
// Fetch data directly from consul and vault. Ignore errors
|
||||
var consul, vault string
|
||||
|
||||
if self != nil {
|
||||
r, ok := self.Config["Consul"]
|
||||
if ok {
|
||||
m, ok := r.(map[string]interface{})
|
||||
if ok {
|
||||
|
||||
raw := m["Addr"]
|
||||
consul, _ = raw.(string)
|
||||
raw = m["EnableSSL"]
|
||||
ssl, _ := raw.(bool)
|
||||
if ssl {
|
||||
consul = "https://" + consul
|
||||
} else {
|
||||
consul = "http://" + consul
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r, ok = self.Config["Vault"]
|
||||
if ok {
|
||||
m, ok := r.(map[string]interface{})
|
||||
if ok {
|
||||
raw := m["Addr"]
|
||||
vault, _ = raw.(string)
|
||||
}
|
||||
}
|
||||
// Collect data from Consul
|
||||
if c.consul.addrVal == "" {
|
||||
c.getConsulAddrFromSelf(self)
|
||||
}
|
||||
c.collectConsul(clusterDir)
|
||||
|
||||
// Collect data from Vault
|
||||
vaultAddr := c.vault.addrVal
|
||||
if vaultAddr == "" {
|
||||
vaultAddr = c.getVaultAddrFromSelf(self)
|
||||
}
|
||||
c.collectVault(clusterDir, vaultAddr)
|
||||
|
||||
c.collectConsul(clusterDir, consul)
|
||||
c.collectVault(clusterDir, vault)
|
||||
c.collectAgentHosts(client)
|
||||
c.collectPprofs(client)
|
||||
|
||||
|
@ -894,42 +880,94 @@ func (c *OperatorDebugCommand) collectNomad(dir string, client *api.Client) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
// collectConsul calls the Consul API directly to collect data
|
||||
func (c *OperatorDebugCommand) collectConsul(dir, consul string) error {
|
||||
addr := c.consul.addr(consul)
|
||||
if addr == "" {
|
||||
return nil
|
||||
// collectConsul calls the Consul API to collect data
|
||||
func (c *OperatorDebugCommand) collectConsul(dir string) {
|
||||
if c.consul.addrVal == "" {
|
||||
c.Ui.Output("Consul - Skipping, no API address found")
|
||||
return
|
||||
}
|
||||
|
||||
client := defaultHttpClient()
|
||||
api.ConfigureTLS(client, c.consul.tls)
|
||||
c.Ui.Info(fmt.Sprintf("Consul - Collecting Consul API data from: %s", c.consul.addrVal))
|
||||
|
||||
client, err := c.consulAPIClient()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to create Consul API client: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Exit if we are unable to retrieve the leader
|
||||
err = c.collectConsulAPIRequest(client, "/v1/status/leader", dir, "consul-leader.json")
|
||||
if err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("Unable to contact Consul leader, skipping: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
c.collectConsulAPI(client, "/v1/agent/host", dir, "consul-agent-host.json")
|
||||
c.collectConsulAPI(client, "/v1/agent/members", dir, "consul-agent-members.json")
|
||||
c.collectConsulAPI(client, "/v1/agent/metrics", dir, "consul-agent-metrics.json")
|
||||
c.collectConsulAPI(client, "/v1/agent/self", dir, "consul-agent-self.json")
|
||||
}
|
||||
|
||||
func (c *OperatorDebugCommand) consulAPIClient() (*http.Client, error) {
|
||||
httpClient := defaultHttpClient()
|
||||
|
||||
err := api.ConfigureTLS(httpClient, c.consul.tls)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to configure TLS: %w", err)
|
||||
}
|
||||
|
||||
return httpClient, nil
|
||||
}
|
||||
|
||||
func (c *OperatorDebugCommand) collectConsulAPI(client *http.Client, urlPath string, dir string, file string) {
|
||||
err := c.collectConsulAPIRequest(client, urlPath, dir, file)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error collecting from Consul API: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OperatorDebugCommand) collectConsulAPIRequest(client *http.Client, urlPath string, dir string, file string) error {
|
||||
url := c.consul.addrVal + urlPath
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create HTTP request for Consul API URL=%q: %w", url, err)
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", addr+"/v1/agent/self", nil)
|
||||
req.Header.Add("X-Consul-Token", c.consul.token())
|
||||
req.Header.Add("User-Agent", userAgent)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
c.writeBody(dir, "consul-agent-self.json", resp, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, _ = http.NewRequest("GET", addr+"/v1/agent/members", nil)
|
||||
req.Header.Add("X-Consul-Token", c.consul.token())
|
||||
req.Header.Add("User-Agent", userAgent)
|
||||
resp, err = client.Do(req)
|
||||
c.writeBody(dir, "consul-agent-members.json", resp, err)
|
||||
c.writeBody(dir, file, resp, err)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// collectVault calls the Vault API directly to collect data
|
||||
func (c *OperatorDebugCommand) collectVault(dir, vault string) error {
|
||||
addr := c.vault.addr(vault)
|
||||
if addr == "" {
|
||||
vaultAddr := c.vault.addr(vault)
|
||||
if vaultAddr == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Ui.Info(fmt.Sprintf("Vault - Collecting Vault API data from: %s", vaultAddr))
|
||||
client := defaultHttpClient()
|
||||
api.ConfigureTLS(client, c.vault.tls)
|
||||
if c.vault.ssl {
|
||||
err := api.ConfigureTLS(client, c.vault.tls)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to configure TLS: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", vaultAddr+"/v1/sys/health", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create HTTP request for Vault API URL=%q: %w", vaultAddr, err)
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", addr+"/sys/health", nil)
|
||||
req.Header.Add("X-Vault-Token", c.vault.token())
|
||||
req.Header.Add("User-Agent", userAgent)
|
||||
resp, err := client.Do(req)
|
||||
|
@ -1028,6 +1066,46 @@ func (c *OperatorDebugCommand) writeBody(dir, file string, resp *http.Response,
|
|||
}
|
||||
}
|
||||
|
||||
type flagExport struct {
|
||||
Name string
|
||||
Parsed bool
|
||||
Actual map[string]*flag.Flag
|
||||
Formal map[string]*flag.Flag
|
||||
Effective map[string]*flag.Flag // All flags with non-empty value
|
||||
Args []string // arguments after flags
|
||||
OsArgs []string
|
||||
}
|
||||
|
||||
// writeFlags exports the CLI flags to JSON file
|
||||
func (c *OperatorDebugCommand) writeFlags(flags *flag.FlagSet) {
|
||||
// c.writeJSON(clusterDir, "cli-flags-complete.json", flags, nil)
|
||||
|
||||
var f flagExport
|
||||
f.Name = flags.Name()
|
||||
f.Parsed = flags.Parsed()
|
||||
f.Formal = make(map[string]*flag.Flag)
|
||||
f.Actual = make(map[string]*flag.Flag)
|
||||
f.Effective = make(map[string]*flag.Flag)
|
||||
f.Args = flags.Args()
|
||||
f.OsArgs = os.Args
|
||||
|
||||
// Formal flags (all flags)
|
||||
flags.VisitAll(func(flagA *flag.Flag) {
|
||||
f.Formal[flagA.Name] = flagA
|
||||
|
||||
// Determine which of thees are "effective" flags by comparing to empty string
|
||||
if flagA.Value.String() != "" {
|
||||
f.Effective[flagA.Name] = flagA
|
||||
}
|
||||
})
|
||||
// Actual flags (everything passed on cmdline)
|
||||
flags.Visit(func(flag *flag.Flag) {
|
||||
f.Actual[flag.Name] = flag
|
||||
})
|
||||
|
||||
c.writeJSON(clusterDir, "cli-flags.json", f, nil)
|
||||
}
|
||||
|
||||
// writeManifest creates the index files
|
||||
func (c *OperatorDebugCommand) writeManifest() error {
|
||||
// Write the JSON
|
||||
|
@ -1214,27 +1292,34 @@ func (e *external) addr(defaultAddr string) string {
|
|||
return defaultAddr
|
||||
}
|
||||
|
||||
if !e.ssl {
|
||||
if strings.HasPrefix(e.addrVal, "http:") {
|
||||
return e.addrVal
|
||||
}
|
||||
if strings.HasPrefix(e.addrVal, "https:") {
|
||||
// Mismatch: e.ssl=false but addrVal is https
|
||||
return strings.ReplaceAll(e.addrVal, "https://", "http://")
|
||||
}
|
||||
return "http://" + e.addrVal
|
||||
}
|
||||
|
||||
if strings.HasPrefix(e.addrVal, "https:") {
|
||||
// Return address as-is if it contains a protocol
|
||||
if strings.Contains(e.addrVal, "://") {
|
||||
return e.addrVal
|
||||
}
|
||||
|
||||
if strings.HasPrefix(e.addrVal, "http:") {
|
||||
// Mismatch: e.ssl=true but addrVal is http
|
||||
return strings.ReplaceAll(e.addrVal, "http://", "https://")
|
||||
if e.ssl {
|
||||
return "https://" + e.addrVal
|
||||
}
|
||||
|
||||
return "https://" + e.addrVal
|
||||
return "http://" + e.addrVal
|
||||
}
|
||||
|
||||
func (e *external) setAddr(addr string) {
|
||||
// Handle no protocol scenario first
|
||||
if !strings.Contains(addr, "://") {
|
||||
e.addrVal = "http://" + addr
|
||||
if e.ssl {
|
||||
e.addrVal = "https://" + addr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Set SSL boolean based on protocol
|
||||
e.ssl = false
|
||||
if strings.Contains(addr, "https") {
|
||||
e.ssl = true
|
||||
}
|
||||
e.addrVal = addr
|
||||
}
|
||||
|
||||
func (e *external) token() string {
|
||||
|
@ -1252,6 +1337,56 @@ func (e *external) token() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (c *OperatorDebugCommand) getConsulAddrFromSelf(self *api.AgentSelf) string {
|
||||
if self == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var consulAddr string
|
||||
r, ok := self.Config["Consul"]
|
||||
if ok {
|
||||
m, ok := r.(map[string]interface{})
|
||||
if ok {
|
||||
raw := m["EnableSSL"]
|
||||
c.consul.ssl, _ = raw.(bool)
|
||||
raw = m["Addr"]
|
||||
c.consul.setAddr(raw.(string))
|
||||
raw = m["Auth"]
|
||||
c.consul.auth, _ = raw.(string)
|
||||
raw = m["Token"]
|
||||
c.consul.tokenVal = raw.(string)
|
||||
|
||||
consulAddr = c.consul.addr("")
|
||||
}
|
||||
}
|
||||
return consulAddr
|
||||
}
|
||||
|
||||
func (c *OperatorDebugCommand) getVaultAddrFromSelf(self *api.AgentSelf) string {
|
||||
if self == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var vaultAddr string
|
||||
r, ok := self.Config["Vault"]
|
||||
if ok {
|
||||
m, ok := r.(map[string]interface{})
|
||||
if ok {
|
||||
raw := m["EnableSSL"]
|
||||
c.vault.ssl, _ = raw.(bool)
|
||||
raw = m["Addr"]
|
||||
c.vault.setAddr(raw.(string))
|
||||
raw = m["Auth"]
|
||||
c.vault.auth, _ = raw.(string)
|
||||
raw = m["Token"]
|
||||
c.vault.tokenVal = raw.(string)
|
||||
|
||||
vaultAddr = c.vault.addr("")
|
||||
}
|
||||
}
|
||||
return vaultAddr
|
||||
}
|
||||
|
||||
// defaultHttpClient configures a basic httpClient
|
||||
func defaultHttpClient() *http.Client {
|
||||
httpClient := cleanhttp.DefaultClient()
|
||||
|
|
|
@ -2,11 +2,16 @@ package command
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
consultest "github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
clienttest "github.com/hashicorp/nomad/client/testutil"
|
||||
"github.com/hashicorp/nomad/command/agent"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/nomad/state"
|
||||
|
@ -159,65 +164,86 @@ func TestDebug_ClientToServer(t *testing.T) {
|
|||
runTestCases(t, cases)
|
||||
}
|
||||
|
||||
func TestDebug_ClientToServer_Region(t *testing.T) {
|
||||
region := "testregion"
|
||||
func TestDebug_MultiRegion(t *testing.T) {
|
||||
region1 := "region1"
|
||||
region2 := "region2"
|
||||
|
||||
// Start test server and API client
|
||||
srv, _, url := testServer(t, false, func(c *agent.Config) {
|
||||
c.Region = region
|
||||
})
|
||||
// Start region1 server
|
||||
server1, _, addrServer1 := testServer(t, false, func(c *agent.Config) { c.Region = region1 })
|
||||
testutil.WaitForLeader(t, server1.Agent.RPC)
|
||||
rpcAddrServer1 := server1.GetConfig().AdvertiseAddrs.RPC
|
||||
t.Logf("[TEST] %s: Leader started, HTTPAddr: %s, RPC: %s", region1, addrServer1, rpcAddrServer1)
|
||||
|
||||
// Wait for leadership to establish
|
||||
testutil.WaitForLeader(t, srv.Agent.RPC)
|
||||
// Start region1 client
|
||||
agent1, _, addrClient1 := testClient(t, "client1", newClientAgentConfigFunc(region1, "", rpcAddrServer1))
|
||||
nodeIdClient1 := agent1.Agent.Client().NodeID()
|
||||
t.Logf("[TEST] %s: Client1 started, ID: %s, HTTPAddr: %s", region1, nodeIdClient1, addrClient1)
|
||||
|
||||
// Retrieve server RPC address to join client
|
||||
srvRPCAddr := srv.GetConfig().AdvertiseAddrs.RPC
|
||||
t.Logf("[TEST] Leader started, srv.GetConfig().AdvertiseAddrs.RPC: %s", srvRPCAddr)
|
||||
// Start region2 server
|
||||
server2, _, addrServer2 := testServer(t, false, func(c *agent.Config) { c.Region = region2 })
|
||||
testutil.WaitForLeader(t, server2.Agent.RPC)
|
||||
rpcAddrServer2 := server2.GetConfig().AdvertiseAddrs.RPC
|
||||
t.Logf("[TEST] %s: Leader started, HTTPAddr: %s, RPC: %s", region2, addrServer2, rpcAddrServer2)
|
||||
|
||||
// Start client
|
||||
agent1, _, _ := testClient(t, "client1", newClientAgentConfigFunc(region, "", srvRPCAddr))
|
||||
// Start client2
|
||||
agent2, _, addrClient2 := testClient(t, "client2", newClientAgentConfigFunc(region2, "", rpcAddrServer2))
|
||||
nodeIdClient2 := agent2.Agent.Client().NodeID()
|
||||
t.Logf("[TEST] %s: Client1 started, ID: %s, HTTPAddr: %s", region2, nodeIdClient2, addrClient2)
|
||||
|
||||
// Get API addresses
|
||||
addrServer := srv.HTTPAddr()
|
||||
addrClient1 := agent1.HTTPAddr()
|
||||
|
||||
t.Logf("[TEST] testAgent api address: %s", url)
|
||||
t.Logf("[TEST] Server api address: %s", addrServer)
|
||||
t.Logf("[TEST] Client1 api address: %s", addrClient1)
|
||||
t.Logf("[TEST] Region: %s, Server1 api address: %s", region1, addrServer1)
|
||||
t.Logf("[TEST] Region: %s, Client1 api address: %s", region1, addrClient1)
|
||||
t.Logf("[TEST] Region: %s, Server2 api address: %s", region2, addrServer2)
|
||||
t.Logf("[TEST] Region: %s, Client2 api address: %s", region2, addrClient2)
|
||||
|
||||
// Setup test cases
|
||||
var cases = testCases{
|
||||
// Good
|
||||
{
|
||||
name: "region - testAgent api server",
|
||||
args: []string{"-address", url, "-region", region, "-duration", "250ms", "-interval", "250ms", "-server-id", "all", "-node-id", "all"},
|
||||
name: "no region - all servers, all clients",
|
||||
args: []string{"-address", addrServer1, "-duration", "250ms", "-interval", "250ms", "-server-id", "all", "-node-id", "all", "-pprof-duration", "0"},
|
||||
expectedCode: 0,
|
||||
},
|
||||
{
|
||||
name: "region1 - server1 address",
|
||||
args: []string{"-address", addrServer1, "-region", region1, "-duration", "50ms", "-interval", "50ms", "-server-id", "all", "-node-id", "all", "-pprof-duration", "0"},
|
||||
expectedCode: 0,
|
||||
expectedOutputs: []string{
|
||||
"Region: " + region + "\n",
|
||||
"Servers: (1/1)",
|
||||
"Clients: (1/1)",
|
||||
"Region: " + region1 + "\n",
|
||||
"Servers: (1/1) [TestDebug_MultiRegion.region1]",
|
||||
"Clients: (1/1) [" + nodeIdClient1 + "]",
|
||||
"Created debug archive",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "region - server address",
|
||||
args: []string{"-address", addrServer, "-region", region, "-duration", "250ms", "-interval", "250ms", "-server-id", "all", "-node-id", "all"},
|
||||
name: "region1 - client1 address",
|
||||
args: []string{"-address", addrClient1, "-region", region1, "-duration", "50ms", "-interval", "50ms", "-server-id", "all", "-node-id", "all", "-pprof-duration", "0"},
|
||||
expectedCode: 0,
|
||||
expectedOutputs: []string{
|
||||
"Region: " + region + "\n",
|
||||
"Servers: (1/1)",
|
||||
"Clients: (1/1)",
|
||||
"Region: " + region1 + "\n",
|
||||
"Servers: (1/1) [TestDebug_MultiRegion.region1]",
|
||||
"Clients: (1/1) [" + nodeIdClient1 + "]",
|
||||
"Created debug archive",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "region - client1 address - verify no SIGSEGV panic",
|
||||
args: []string{"-address", addrClient1, "-region", region, "-duration", "250ms", "-interval", "250ms", "-server-id", "all", "-node-id", "all"},
|
||||
name: "region2 - server2 address",
|
||||
args: []string{"-address", addrServer2, "-region", region2, "-duration", "50ms", "-interval", "50ms", "-server-id", "all", "-node-id", "all", "-pprof-duration", "0"},
|
||||
expectedCode: 0,
|
||||
expectedOutputs: []string{
|
||||
"Region: " + region + "\n",
|
||||
"Servers: (1/1)",
|
||||
"Clients: (1/1)",
|
||||
"Region: " + region2 + "\n",
|
||||
"Servers: (1/1) [TestDebug_MultiRegion.region2]",
|
||||
"Clients: (1/1) [" + nodeIdClient2 + "]",
|
||||
"Created debug archive",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "region2 - client2 address",
|
||||
args: []string{"-address", addrClient2, "-region", region2, "-duration", "50ms", "-interval", "50ms", "-server-id", "all", "-node-id", "all", "-pprof-duration", "0"},
|
||||
expectedCode: 0,
|
||||
expectedOutputs: []string{
|
||||
"Region: " + region2 + "\n",
|
||||
"Servers: (1/1) [TestDebug_MultiRegion.region2]",
|
||||
"Clients: (1/1) [" + nodeIdClient2 + "]",
|
||||
"Created debug archive",
|
||||
},
|
||||
},
|
||||
|
@ -225,7 +251,7 @@ func TestDebug_ClientToServer_Region(t *testing.T) {
|
|||
// Bad
|
||||
{
|
||||
name: "invalid region - all servers, all clients",
|
||||
args: []string{"-address", url, "-region", "never", "-duration", "250ms", "-interval", "250ms", "-server-id", "all", "-node-id", "all"},
|
||||
args: []string{"-address", addrServer1, "-region", "never", "-duration", "50ms", "-interval", "50ms", "-server-id", "all", "-node-id", "all", "-pprof-duration", "0"},
|
||||
expectedCode: 1,
|
||||
expectedError: "500 (No path to region)",
|
||||
},
|
||||
|
@ -375,12 +401,9 @@ func TestDebug_CapturedFiles(t *testing.T) {
|
|||
// Setup file slices
|
||||
clusterFiles := []string{
|
||||
"agent-self.json",
|
||||
"consul-agent-members.json",
|
||||
"consul-agent-self.json",
|
||||
"members.json",
|
||||
"namespaces.json",
|
||||
"regions.json",
|
||||
"vault-sys-health.json",
|
||||
}
|
||||
|
||||
pprofFiles := []string{
|
||||
|
@ -424,13 +447,18 @@ func TestDebug_CapturedFiles(t *testing.T) {
|
|||
ui := cli.NewMockUi()
|
||||
cmd := &OperatorDebugCommand{Meta: Meta{Ui: ui}}
|
||||
|
||||
duration := 2 * time.Second
|
||||
interval := 750 * time.Millisecond
|
||||
waitTime := 2 * duration
|
||||
|
||||
code := cmd.Run([]string{
|
||||
"-address", url,
|
||||
"-output", os.TempDir(),
|
||||
"-server-id", serverName,
|
||||
"-node-id", clientID,
|
||||
"-duration", "1300ms",
|
||||
"-interval", "600ms",
|
||||
"-duration", duration.String(),
|
||||
"-interval", interval.String(),
|
||||
"-pprof-duration", "0",
|
||||
})
|
||||
|
||||
// Get capture directory
|
||||
|
@ -445,27 +473,27 @@ func TestDebug_CapturedFiles(t *testing.T) {
|
|||
// Verify cluster files
|
||||
clusterPaths := buildPathSlice(cmd.path(clusterDir), clusterFiles)
|
||||
t.Logf("Waiting for cluster files in path: %s", clusterDir)
|
||||
testutil.WaitForFilesUntil(t, clusterPaths, 2*time.Minute)
|
||||
testutil.WaitForFilesUntil(t, clusterPaths, waitTime)
|
||||
|
||||
// Verify client files
|
||||
clientPaths := buildPathSlice(cmd.path(clientDir, clientID), clientFiles)
|
||||
t.Logf("Waiting for client files in path: %s", clientDir)
|
||||
testutil.WaitForFilesUntil(t, clientPaths, 2*time.Minute)
|
||||
testutil.WaitForFilesUntil(t, clientPaths, waitTime)
|
||||
|
||||
// Verify server files
|
||||
serverPaths := buildPathSlice(cmd.path(serverDir, serverName), serverFiles)
|
||||
t.Logf("Waiting for server files in path: %s", serverDir)
|
||||
testutil.WaitForFilesUntil(t, serverPaths, 2*time.Minute)
|
||||
testutil.WaitForFilesUntil(t, serverPaths, waitTime)
|
||||
|
||||
// Verify interval 0000 files
|
||||
intervalPaths0 := buildPathSlice(cmd.path(intervalDir, "0000"), intervalFiles)
|
||||
t.Logf("Waiting for interval 0000 files in path: %s", intervalDir)
|
||||
testutil.WaitForFilesUntil(t, intervalPaths0, 2*time.Minute)
|
||||
testutil.WaitForFilesUntil(t, intervalPaths0, waitTime)
|
||||
|
||||
// Verify interval 0001 files
|
||||
intervalPaths1 := buildPathSlice(cmd.path(intervalDir, "0001"), intervalFiles)
|
||||
t.Logf("Waiting for interval 0001 files in path: %s", intervalDir)
|
||||
testutil.WaitForFilesUntil(t, intervalPaths1, 2*time.Minute)
|
||||
testutil.WaitForFilesUntil(t, intervalPaths1, waitTime)
|
||||
}
|
||||
|
||||
func TestDebug_ExistingOutput(t *testing.T) {
|
||||
|
@ -538,9 +566,10 @@ func TestDebug_External(t *testing.T) {
|
|||
require.Equal(t, "https://127.0.0.1:8500", addr)
|
||||
|
||||
// ssl: true - protocol incorrect
|
||||
// NOTE: Address with protocol now overrides ssl flag
|
||||
e = &external{addrVal: "http://127.0.0.1:8500", ssl: true}
|
||||
addr = e.addr("foo")
|
||||
require.Equal(t, "https://127.0.0.1:8500", addr)
|
||||
require.Equal(t, "http://127.0.0.1:8500", addr)
|
||||
|
||||
// ssl: true - protocol missing
|
||||
e = &external{addrVal: "127.0.0.1:8500", ssl: true}
|
||||
|
@ -553,14 +582,20 @@ func TestDebug_External(t *testing.T) {
|
|||
require.Equal(t, "http://127.0.0.1:8500", addr)
|
||||
|
||||
// ssl: false - protocol incorrect
|
||||
// NOTE: Address with protocol now overrides ssl flag
|
||||
e = &external{addrVal: "https://127.0.0.1:8500", ssl: false}
|
||||
addr = e.addr("foo")
|
||||
require.Equal(t, "http://127.0.0.1:8500", addr)
|
||||
require.Equal(t, "https://127.0.0.1:8500", addr)
|
||||
|
||||
// ssl: false - protocol missing
|
||||
e = &external{addrVal: "127.0.0.1:8500", ssl: false}
|
||||
addr = e.addr("foo")
|
||||
require.Equal(t, "http://127.0.0.1:8500", addr)
|
||||
|
||||
// Address through proxy might not have a port
|
||||
e = &external{addrVal: "https://127.0.0.1", ssl: true}
|
||||
addr = e.addr("foo")
|
||||
require.Equal(t, "https://127.0.0.1", addr)
|
||||
}
|
||||
|
||||
func TestDebug_WriteBytes_Nil(t *testing.T) {
|
||||
|
@ -608,3 +643,100 @@ func TestDebug_WriteBytes_PathEscapesSandbox(t *testing.T) {
|
|||
err := cmd.writeBytes(testDir, testFile, testBytes)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestDebug_CollectConsul(t *testing.T) {
|
||||
t.Parallel()
|
||||
if testing.Short() {
|
||||
t.Skip("-short set; skipping")
|
||||
}
|
||||
|
||||
// Skip test if Consul binary cannot be found
|
||||
clienttest.RequireConsul(t)
|
||||
|
||||
// Create an embedded Consul server
|
||||
testconsul, err := consultest.NewTestServerConfigT(t, func(c *consultest.TestServerConfig) {
|
||||
// If -v wasn't specified squelch consul logging
|
||||
if !testing.Verbose() {
|
||||
c.Stdout = ioutil.Discard
|
||||
c.Stderr = ioutil.Discard
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
t.Fatalf("error starting test consul server: %v", err)
|
||||
}
|
||||
defer testconsul.Stop()
|
||||
|
||||
consulConfig := consulapi.DefaultConfig()
|
||||
consulConfig.Address = testconsul.HTTPAddr
|
||||
|
||||
// Setup mock UI
|
||||
ui := cli.NewMockUi()
|
||||
c := &OperatorDebugCommand{Meta: Meta{Ui: ui}}
|
||||
|
||||
// Setup Consul *external
|
||||
ce := &external{}
|
||||
ce.setAddr(consulConfig.Address)
|
||||
if ce.ssl {
|
||||
ce.tls = &api.TLSConfig{}
|
||||
}
|
||||
|
||||
// Set global client
|
||||
c.consul = ce
|
||||
|
||||
// Setup capture directory
|
||||
testDir := os.TempDir()
|
||||
defer os.Remove(testDir)
|
||||
c.collectDir = testDir
|
||||
|
||||
// Collect data from Consul into folder "test"
|
||||
c.collectConsul("test")
|
||||
|
||||
require.Empty(t, ui.ErrorWriter.String())
|
||||
require.FileExists(t, filepath.Join(testDir, "test", "consul-agent-host.json"))
|
||||
require.FileExists(t, filepath.Join(testDir, "test", "consul-agent-members.json"))
|
||||
require.FileExists(t, filepath.Join(testDir, "test", "consul-agent-metrics.json"))
|
||||
require.FileExists(t, filepath.Join(testDir, "test", "consul-leader.json"))
|
||||
}
|
||||
|
||||
func TestDebug_CollectVault(t *testing.T) {
|
||||
t.Parallel()
|
||||
if testing.Short() {
|
||||
t.Skip("-short set; skipping")
|
||||
}
|
||||
|
||||
// Skip test if Consul binary cannot be found
|
||||
clienttest.RequireVault(t)
|
||||
|
||||
// Create a Vault server
|
||||
v := testutil.NewTestVault(t)
|
||||
defer v.Stop()
|
||||
|
||||
// Setup mock UI
|
||||
ui := cli.NewMockUi()
|
||||
c := &OperatorDebugCommand{Meta: Meta{Ui: ui}}
|
||||
|
||||
// Setup Vault *external
|
||||
ve := &external{}
|
||||
ve.tokenVal = v.RootToken
|
||||
ve.setAddr(v.HTTPAddr)
|
||||
if ve.ssl {
|
||||
ve.tls = &api.TLSConfig{}
|
||||
}
|
||||
|
||||
// Set global client
|
||||
c.vault = ve
|
||||
|
||||
// Set capture directory
|
||||
testDir := os.TempDir()
|
||||
defer os.Remove(testDir)
|
||||
c.collectDir = testDir
|
||||
|
||||
// Collect data from Vault
|
||||
err := c.collectVault("test", "")
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, ui.ErrorWriter.String())
|
||||
|
||||
require.FileExists(t, filepath.Join(testDir, "test", "vault-sys-health.json"))
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ func NewTestVaultFromPath(t testing.T, binary string) *TestVault {
|
|||
|
||||
}
|
||||
|
||||
// NewTestVault returns a new TestVault instance that has yet to be started
|
||||
// NewTestVault returns a new TestVault instance that is ready for API calls
|
||||
func NewTestVault(t testing.T) *TestVault {
|
||||
// Lookup vault from the path
|
||||
return NewTestVaultFromPath(t, "vault")
|
||||
|
|
Loading…
Reference in New Issue