diff --git a/command/agent_info_test.go b/command/agent_info_test.go index d9d7a18c2..d16f04704 100644 --- a/command/agent_info_test.go +++ b/command/agent_info_test.go @@ -12,7 +12,7 @@ func TestAgentInfoCommand_Implements(t *testing.T) { } func TestAgentInfoCommand_Run(t *testing.T) { - srv, _, url := testServer(t) + srv, _, url := testServer(t, nil) defer srv.Stop() ui := new(cli.MockUi) diff --git a/command/agent_members_test.go b/command/agent_members_test.go index 0136e0dd5..d7a22df05 100644 --- a/command/agent_members_test.go +++ b/command/agent_members_test.go @@ -12,7 +12,7 @@ func TestAgentMembersCommand_Implements(t *testing.T) { } func TestAgentMembersCommand_Run(t *testing.T) { - srv, client, url := testServer(t) + srv, client, url := testServer(t, nil) defer srv.Stop() ui := new(cli.MockUi) diff --git a/command/node_drain_test.go b/command/node_drain_test.go index 11bd04da8..fa437efb7 100644 --- a/command/node_drain_test.go +++ b/command/node_drain_test.go @@ -12,7 +12,7 @@ func TestNodeDrainCommand_Implements(t *testing.T) { } func TestNodeDrainCommand_Fails(t *testing.T) { - srv, _, url := testServer(t) + srv, _, url := testServer(t, nil) defer srv.Stop() ui := new(cli.MockUi) diff --git a/command/node_status.go b/command/node_status.go index 4c6f809f5..438f4aa45 100644 --- a/command/node_status.go +++ b/command/node_status.go @@ -25,7 +25,14 @@ Usage: nomad node-status [options] [node] General Options: - ` + generalOptionsUsage() + ` + generalOptionsUsage() + ` + +Node Status Options: + + -short + Display short output. Used only when a single node is being + queried, and drops verbose output about node allocations. +` return strings.TrimSpace(helpText) } @@ -34,8 +41,12 @@ func (c *NodeStatusCommand) Synopsis() string { } func (c *NodeStatusCommand) Run(args []string) int { + var short bool + flags := c.Meta.FlagSet("node-status", FlagSetClient) flags.Usage = func() { c.Ui.Output(c.Help()) } + flags.BoolVar(&short, "short", false, "") + if err := flags.Parse(args); err != nil { return 1 } @@ -94,8 +105,12 @@ func (c *NodeStatusCommand) Run(args []string) int { return 1 } + // Make the column config so we can dump k = v pairs + columnConf := columnize.DefaultConfig() + columnConf.Glue = " = " + // Format the output - out := []string{ + basic := []string{ fmt.Sprintf("ID | %s", node.ID), fmt.Sprintf("Name | %s", node.Name), fmt.Sprintf("Class | %s", node.NodeClass), @@ -104,11 +119,34 @@ func (c *NodeStatusCommand) Run(args []string) int { fmt.Sprintf("Status | %s", node.Status), } - // Make the column config so we can dump k = v pairs - columnConf := columnize.DefaultConfig() - columnConf.Glue = " = " + var allocs []string + if !short { + // Query the node allocations + nodeAllocs, _, err := client.Nodes().Allocations(nodeID, nil) + if err != nil { + c.Ui.Error(fmt.Sprintf("Error querying node allocations: %s", err)) + return 1 + } + + // Format the allocations + allocs = make([]string, len(nodeAllocs)+1) + allocs[0] = "ID|EvalID|JobID|TaskGroup|DesiredStatus|ClientStatus" + for i, alloc := range nodeAllocs { + allocs[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s", + alloc.ID, + alloc.EvalID, + alloc.JobID, + alloc.TaskGroup, + alloc.DesiredStatus, + alloc.ClientStatus) + } + } // Dump the output - c.Ui.Output(columnize.Format(out, columnConf)) + c.Ui.Output(columnize.Format(basic, columnConf)) + if !short { + c.Ui.Output("\nAllocations") + c.Ui.Output(columnize.SimpleFormat(allocs)) + } return 0 } diff --git a/command/node_status_test.go b/command/node_status_test.go index 5b8965641..567a91f55 100644 --- a/command/node_status_test.go +++ b/command/node_status_test.go @@ -1,9 +1,11 @@ package command import ( + "fmt" "strings" "testing" + "github.com/hashicorp/nomad/testutil" "github.com/mitchellh/cli" ) @@ -12,25 +14,70 @@ func TestNodeStatusCommand_Implements(t *testing.T) { } func TestNodeStatusCommand_Run(t *testing.T) { - srv, _, url := testServer(t) + // Start in dev mode so we get a node registration + srv, client, url := testServer(t, func(c *testutil.TestServerConfig) { + c.DevMode = true + c.NodeName = "mynode" + }) defer srv.Stop() ui := new(cli.MockUi) cmd := &NodeStatusCommand{Meta: Meta{Ui: ui}} + // Wait for a node to appear + var nodeID string + testutil.WaitForResult(func() (bool, error) { + nodes, _, err := client.Nodes().List(nil) + if err != nil { + return false, err + } + if len(nodes) == 0 { + return false, fmt.Errorf("missing node") + } + nodeID = nodes[0].ID + return true, nil + }, func(err error) { + t.Fatalf("err: %s", err) + }) + // Query all node statuses if code := cmd.Run([]string{"-address=" + url}); code != 0 { t.Fatalf("expected exit 0, got: %d", code) } + out := ui.OutputWriter.String() + if !strings.Contains(out, "mynode") { + t.Fatalf("expect to find mynode, got: %s", out) + } + ui.OutputWriter.Reset() - // Expect empty output since we have no nodes - if out := ui.OutputWriter.String(); out != "" { - t.Fatalf("expected empty output, got: %s", out) + // Query a single node + if code := cmd.Run([]string{"-address=" + url, nodeID}); code != 0 { + t.Fatalf("expected exit 0, got: %d", code) + } + out = ui.OutputWriter.String() + if !strings.Contains(out, "mynode") { + t.Fatalf("expect to find mynode, got: %s", out) + } + if !strings.Contains(out, "Allocations") { + t.Fatalf("expected allocations, got: %s", out) + } + ui.OutputWriter.Reset() + + // Query single node in short view + if code := cmd.Run([]string{"-address=" + url, "-short", nodeID}); code != 0 { + t.Fatalf("expected exit 0, got: %d", code) + } + out = ui.OutputWriter.String() + if !strings.Contains(out, "mynode") { + t.Fatalf("expect to find mynode, got: %s", out) + } + if strings.Contains(out, "Allocations") { + t.Fatalf("should not dump allocations") } } func TestNodeStatusCommand_Fails(t *testing.T) { - srv, _, url := testServer(t) + srv, _, url := testServer(t, nil) defer srv.Stop() ui := new(cli.MockUi) diff --git a/command/status_test.go b/command/status_test.go index a50489c55..bae989172 100644 --- a/command/status_test.go +++ b/command/status_test.go @@ -13,7 +13,7 @@ func TestStatusCommand_Implements(t *testing.T) { } func TestStatusCommand_Run(t *testing.T) { - srv, client, url := testServer(t) + srv, client, url := testServer(t, nil) defer srv.Stop() ui := new(cli.MockUi) diff --git a/command/util_test.go b/command/util_test.go index 82686fdb0..16fb62c91 100644 --- a/command/util_test.go +++ b/command/util_test.go @@ -15,7 +15,10 @@ func init() { seen = make(map[*testing.T]struct{}) } -func testServer(t *testing.T) (*testutil.TestServer, *api.Client, string) { +func testServer( + t *testing.T, + cb testutil.ServerConfigCallback) (*testutil.TestServer, *api.Client, string) { + // Always run these tests in parallel. if _, ok := seen[t]; !ok { seen[t] = struct{}{} @@ -23,7 +26,7 @@ func testServer(t *testing.T) (*testutil.TestServer, *api.Client, string) { } // Make a new test server - srv := testutil.NewTestServer(t, nil) + srv := testutil.NewTestServer(t, cb) // Make a client clientConf := api.DefaultConfig()