cleanup: fixing warnings and refactoring of command package, part 2

This PR continues the cleanup of the command package, removing linter
warnings, refactoring to use helpers, making tests easier to read, etc.
This commit is contained in:
Seth Hoenig 2022-08-18 08:51:53 -05:00
parent c27fa4f52a
commit c5d36eaa2f
12 changed files with 441 additions and 554 deletions

View File

@ -1,11 +1,11 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/nomad/ci"
"github.com/mitchellh/cli"
"github.com/shoenig/test/must"
)
func TestAgentInfoCommand_Implements(t *testing.T) {
@ -16,49 +16,43 @@ func TestAgentInfoCommand_Implements(t *testing.T) {
func TestAgentInfoCommand_Run(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AgentInfoCommand{Meta: Meta{Ui: ui}}
code := cmd.Run([]string{"-address=" + url})
if code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
must.Zero(t, code)
}
func TestAgentInfoCommand_Run_JSON(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AgentInfoCommand{Meta: Meta{Ui: ui}}
code := cmd.Run([]string{"-address=" + url, "-json"})
if code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
if out := ui.OutputWriter.String(); !strings.Contains(out, "\"config\": {") {
t.Fatalf("expected config stanza in output json")
}
must.Zero(t, code)
out := ui.OutputWriter.String()
must.StrContains(t, out, `"config"`)
}
func TestAgentInfoCommand_Run_Gotemplate(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AgentInfoCommand{Meta: Meta{Ui: ui}}
code := cmd.Run([]string{"-address=" + url, "-t", "{{.Stats.raft}}"})
if code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
if out := ui.OutputWriter.String(); !strings.Contains(out, "last_log_index") {
t.Fatalf("expected raft stats in gotemplate output")
}
must.Zero(t, code)
out := ui.OutputWriter.String()
must.StrContains(t, out, "last_log_index")
}
func TestAgentInfoCommand_Fails(t *testing.T) {
@ -67,19 +61,18 @@ func TestAgentInfoCommand_Fails(t *testing.T) {
cmd := &AgentInfoCommand{Meta: Meta{Ui: ui}}
// Fails on misuse
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
t.Fatalf("expected help output, got: %s", out)
}
code := cmd.Run([]string{"some", "bad", "args"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, commandErrorText(cmd))
ui.ErrorWriter.Reset()
// Fails on connection failure
if code := cmd.Run([]string{"-address=nope"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying agent info") {
t.Fatalf("expected failed query error, got: %s", out)
}
code = cmd.Run([]string{"-address=nope"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Error querying agent info")
}

View File

@ -1,11 +1,11 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/nomad/ci"
"github.com/mitchellh/cli"
"github.com/shoenig/test/must"
)
func TestMonitorCommand_Implements(t *testing.T) {
@ -16,31 +16,27 @@ func TestMonitorCommand_Implements(t *testing.T) {
func TestMonitorCommand_Fails(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &MonitorCommand{Meta: Meta{Ui: ui}}
// Fails on misuse
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
t.Fatalf("exepected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
t.Fatalf("expected help output, got: %s", out)
}
code := cmd.Run([]string{"some", "bad", "args"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, commandErrorText(cmd))
ui.ErrorWriter.Reset()
if code := cmd.Run([]string{"-address=nope"}); code != 1 {
t.Fatalf("exepected exit code 1, got: %d", code)
}
code = cmd.Run([]string{"-address=nope"})
must.One(t, code)
// Fails on nonexistent node
if code := cmd.Run([]string{"-address=" + url, "-node-id=12345678-abcd-efab-cdef-123456789abc"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No node(s) with prefix") {
t.Fatalf("expected not found error, got: %s", out)
}
ui.ErrorWriter.Reset()
code = cmd.Run([]string{"-address=" + url, "-node-id=12345678-abcd-efab-cdef-123456789abc"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No node(s) with prefix")
}

View File

@ -13,8 +13,7 @@ import (
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/shoenig/test/must"
)
// static check
@ -23,7 +22,7 @@ var _ cli.Command = &AllocExecCommand{}
func TestAllocExecCommand_Fails(t *testing.T) {
ci.Parallel(t)
srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
cases := []struct {
name string
@ -83,32 +82,18 @@ func TestAllocExecCommand_Fails(t *testing.T) {
cmd := &AllocExecCommand{Meta: Meta{Ui: ui}}
code := cmd.Run(c.args)
require.Equal(t, 1, code)
must.One(t, code)
require.Contains(t, ui.ErrorWriter.String(), c.expectedError)
out := ui.ErrorWriter.String()
must.StrContains(t, out, c.expectedError)
ui.ErrorWriter.Reset()
ui.OutputWriter.Reset()
})
}
// Wait for a node to be ready
testutil.WaitForResult(func() (bool, error) {
nodes, _, err := client.Nodes().List(nil)
if err != nil {
return false, err
}
for _, node := range nodes {
if _, ok := node.Drivers["mock_driver"]; ok &&
node.Status == structs.NodeStatusReady {
return true, nil
}
}
return false, fmt.Errorf("no ready nodes")
}, func(err error) {
require.NoError(t, err)
})
waitForNodes(t, client)
t.Run("non existent task", func(t *testing.T) {
ui := cli.NewMockUi()
@ -116,10 +101,12 @@ func TestAllocExecCommand_Fails(t *testing.T) {
jobID := "job1_sfx"
job1 := testJob(jobID)
resp, _, err := client.Jobs().Register(job1, nil)
require.NoError(t, err)
must.NoError(t, err)
code := waitForSuccess(ui, client, fullId, t, resp.EvalID)
require.Zero(t, code, "status code not zero")
must.Zero(t, code)
// get an alloc id
allocId1 := ""
@ -128,16 +115,24 @@ func TestAllocExecCommand_Fails(t *testing.T) {
allocId1 = allocs[0].ID
}
}
require.NotEmpty(t, allocId1, "unable to find allocation")
must.NotEq(t, "", allocId1)
// by alloc
require.Equal(t, 1, cmd.Run([]string{"-address=" + url, "-task=nonexistenttask1", allocId1, "/bin/bash"}))
require.Contains(t, ui.ErrorWriter.String(), "Could not find task named: nonexistenttask1")
code = cmd.Run([]string{"-address=" + url, "-task=nonexistenttask1", allocId1, "/bin/bash"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, "Could not find task named: nonexistenttask1")
ui.ErrorWriter.Reset()
// by jobID
require.Equal(t, 1, cmd.Run([]string{"-address=" + url, "-task=nonexistenttask2", "-job", jobID, "/bin/bash"}))
require.Contains(t, ui.ErrorWriter.String(), "Could not find task named: nonexistenttask2")
code = cmd.Run([]string{"-address=" + url, "-task=nonexistenttask2", "-job", jobID, "/bin/bash"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Could not find task named: nonexistenttask2")
ui.ErrorWriter.Reset()
})
@ -145,10 +140,9 @@ func TestAllocExecCommand_Fails(t *testing.T) {
func TestAllocExecCommand_AutocompleteArgs(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
srv, _, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocExecCommand{Meta: Meta{Ui: ui, flagAddress: url}}
@ -156,39 +150,24 @@ func TestAllocExecCommand_AutocompleteArgs(t *testing.T) {
// Create a fake alloc
state := srv.Agent.Server().State()
a := mock.Alloc()
assert.Nil(state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
prefix := a.ID[:5]
args := complete.Args{Last: prefix}
predictor := cmd.AutocompleteArgs()
res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(a.ID, res[0])
must.Len(t, 1, res)
must.Eq(t, a.ID, res[0])
}
func TestAllocExecCommand_Run(t *testing.T) {
ci.Parallel(t)
srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
// Wait for a node to be ready
testutil.WaitForResult(func() (bool, error) {
nodes, _, err := client.Nodes().List(nil)
if err != nil {
return false, err
}
for _, node := range nodes {
if _, ok := node.Drivers["mock_driver"]; ok &&
node.Status == structs.NodeStatusReady {
return true, nil
}
}
return false, fmt.Errorf("no ready nodes")
}, func(err error) {
require.NoError(t, err)
})
waitForNodes(t, client)
jobID := uuid.Generate()
job := testJob(jobID)
@ -202,11 +181,11 @@ func TestAllocExecCommand_Run(t *testing.T) {
},
}
resp, _, err := client.Jobs().Register(job, nil)
require.NoError(t, err)
must.NoError(t, err)
evalUi := cli.NewMockUi()
code := waitForSuccess(evalUi, client, fullId, t, resp.EvalID)
require.Equal(t, 0, code, "failed to get status - output: %v", evalUi.ErrorWriter.String())
must.Zero(t, code)
allocId := ""
@ -227,10 +206,7 @@ func TestAllocExecCommand_Run(t *testing.T) {
allocId = alloc.ID
return true, nil
}, func(err error) {
require.NoError(t, err)
})
}, func(err error) { must.NoError(t, err) })
cases := []struct {
name string
@ -271,9 +247,9 @@ func TestAllocExecCommand_Run(t *testing.T) {
}
code = cmd.Run([]string{"-address=" + url, allocId, c.command})
assert.Equal(t, c.exitCode, code)
assert.Equal(t, c.stdout, strings.TrimSpace(stdout.String()))
assert.Equal(t, c.stderr, strings.TrimSpace(stderr.String()))
must.Eq(t, c.exitCode, code)
must.Eq(t, c.stdout, strings.TrimSpace(stdout.String()))
must.Eq(t, c.stderr, strings.TrimSpace(stderr.String()))
})
t.Run("by job: "+c.name, func(t *testing.T) {
ui := cli.NewMockUi()
@ -287,9 +263,9 @@ func TestAllocExecCommand_Run(t *testing.T) {
}
code = cmd.Run([]string{"-address=" + url, "-job", jobID, c.command})
assert.Equal(t, c.exitCode, code)
assert.Equal(t, c.stdout, strings.TrimSpace(stdout.String()))
assert.Equal(t, c.stderr, strings.TrimSpace(stderr.String()))
must.Eq(t, c.exitCode, code)
must.Eq(t, c.stdout, strings.TrimSpace(stdout.String()))
must.Eq(t, c.stderr, strings.TrimSpace(stderr.String()))
})
}
}

View File

@ -1,7 +1,6 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/nomad/ci"
@ -9,7 +8,7 @@ import (
"github.com/hashicorp/nomad/nomad/structs"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/shoenig/test/must"
)
func TestFSCommand_Implements(t *testing.T) {
@ -20,80 +19,78 @@ func TestFSCommand_Implements(t *testing.T) {
func TestFSCommand_Fails(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocFSCommand{Meta: Meta{Ui: ui}}
// Fails on lack of job ID
if code := cmd.Run([]string{"-job"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "job ID is required") {
t.Fatalf("expected help output, got: %s", out)
}
code := cmd.Run([]string{"-job"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, "job ID is required")
ui.ErrorWriter.Reset()
// Fails on lack of allocation ID
if code := cmd.Run([]string{}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "allocation ID is required") {
t.Fatalf("expected help output, got: %s", out)
}
code = cmd.Run([]string{})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "allocation ID is required")
ui.ErrorWriter.Reset()
// Fails on misuse
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
t.Fatalf("expected help output, got: %s", out)
}
code = cmd.Run([]string{"some", "bad", "args"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, commandErrorText(cmd))
ui.ErrorWriter.Reset()
// Fails on connection failure
if code := cmd.Run([]string{"-address=nope", "foobar"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying allocation") {
t.Fatalf("expected failed query error, got: %s", out)
}
code = cmd.Run([]string{"-address=nope", "foobar"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Error querying allocation")
ui.ErrorWriter.Reset()
// Fails on missing alloc
if code := cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No allocation(s) with prefix or id") {
t.Fatalf("expected not found error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Fail on identifier with too few characters
if code := cmd.Run([]string{"-address=" + url, "2"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "must contain at least two characters.") {
t.Fatalf("expected too few characters error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "2"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "must contain at least two characters.")
ui.ErrorWriter.Reset()
// Identifiers with uneven length should produce a query result
if code := cmd.Run([]string{"-address=" + url, "123"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No allocation(s) with prefix or id") {
t.Fatalf("expected not found error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "123"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
}
func TestFSCommand_AutocompleteArgs(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
srv, _, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocFSCommand{Meta: Meta{Ui: ui, flagAddress: url}}
@ -101,13 +98,13 @@ func TestFSCommand_AutocompleteArgs(t *testing.T) {
// Create a fake alloc
state := srv.Agent.Server().State()
a := mock.Alloc()
assert.Nil(state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
prefix := a.ID[:5]
args := complete.Args{Last: prefix}
predictor := cmd.AutocompleteArgs()
res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(a.ID, res[0])
must.Len(t, 1, res)
must.Eq(t, a.ID, res[0])
}

View File

@ -1,7 +1,6 @@
package command
import (
"strings"
"testing"
"github.com/hashicorp/nomad/ci"
@ -9,7 +8,7 @@ import (
"github.com/hashicorp/nomad/nomad/structs"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/shoenig/test/must"
)
func TestLogsCommand_Implements(t *testing.T) {
@ -20,62 +19,60 @@ func TestLogsCommand_Implements(t *testing.T) {
func TestLogsCommand_Fails(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocLogsCommand{Meta: Meta{Ui: ui}}
// Fails on misuse
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
t.Fatalf("expected help output, got: %s", out)
}
code := cmd.Run([]string{"some", "bad", "args"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, commandErrorText(cmd))
ui.ErrorWriter.Reset()
// Fails on connection failure
if code := cmd.Run([]string{"-address=nope", "foobar"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying allocation") {
t.Fatalf("expected failed query error, got: %s", out)
}
code = cmd.Run([]string{"-address=nope", "foobar"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Error querying allocation")
ui.ErrorWriter.Reset()
// Fails on missing alloc
if code := cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No allocation(s) with prefix or id") {
t.Fatalf("expected not found error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Fail on identifier with too few characters
if code := cmd.Run([]string{"-address=" + url, "2"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "must contain at least two characters.") {
t.Fatalf("expected too few characters error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "2"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "must contain at least two characters.")
ui.ErrorWriter.Reset()
// Identifiers with uneven length should produce a query result
if code := cmd.Run([]string{"-address=" + url, "123"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No allocation(s) with prefix or id") {
t.Fatalf("expected not found error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "123"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
}
func TestLogsCommand_AutocompleteArgs(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
srv, _, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocLogsCommand{Meta: Meta{Ui: ui, flagAddress: url}}
@ -83,13 +80,13 @@ func TestLogsCommand_AutocompleteArgs(t *testing.T) {
// Create a fake alloc
state := srv.Agent.Server().State()
a := mock.Alloc()
assert.Nil(state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
prefix := a.ID[:5]
args := complete.Args{Last: prefix}
predictor := cmd.AutocompleteArgs()
res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(a.ID, res[0])
must.Len(t, 1, res)
must.Eq(t, a.ID, res[0])
}

View File

@ -1,18 +1,14 @@
package command
import (
"fmt"
"testing"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/shoenig/test/must"
)
func TestAllocRestartCommand_Implements(t *testing.T) {
@ -23,61 +19,67 @@ func TestAllocRestartCommand_Fails(t *testing.T) {
ci.Parallel(t)
srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
require := require.New(t)
ui := cli.NewMockUi()
cmd := &AllocRestartCommand{Meta: Meta{Ui: ui}}
// Fails on misuse
require.Equal(cmd.Run([]string{"some", "garbage", "args"}), 1, "Expected failure")
require.Contains(ui.ErrorWriter.String(), commandErrorText(cmd), "Expected help output")
code := cmd.Run([]string{"some", "garbage", "args"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, commandErrorText(cmd))
ui.ErrorWriter.Reset()
// Fails on connection failure
require.Equal(cmd.Run([]string{"-address=nope", "foobar"}), 1, "expected failure")
require.Contains(ui.ErrorWriter.String(), "Error querying allocation")
code = cmd.Run([]string{"-address=nope", "foobar"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Error querying allocation")
ui.ErrorWriter.Reset()
// Fails on missing alloc
require.Equal(cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"}), 1)
require.Contains(ui.ErrorWriter.String(), "No allocation(s) with prefix or id")
code = cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Fail on identifier with too few characters
require.Equal(cmd.Run([]string{"-address=" + url, "2"}), 1)
require.Contains(ui.ErrorWriter.String(), "must contain at least two characters")
code = cmd.Run([]string{"-address=" + url, "2"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "must contain at least two characters")
ui.ErrorWriter.Reset()
// Identifiers with uneven length should produce a query result
require.Equal(cmd.Run([]string{"-address=" + url, "123"}), 1)
require.Contains(ui.ErrorWriter.String(), "No allocation(s) with prefix or id")
code = cmd.Run([]string{"-address=" + url, "123"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Wait for a node to be ready
testutil.WaitForResult(func() (bool, error) {
nodes, _, err := client.Nodes().List(nil)
if err != nil {
return false, err
}
for _, node := range nodes {
if _, ok := node.Drivers["mock_driver"]; ok &&
node.Status == structs.NodeStatusReady {
return true, nil
}
}
return false, fmt.Errorf("no ready nodes")
}, func(err error) {
t.Fatalf("err: %v", err)
})
waitForNodes(t, client)
jobID := "job1_sfx"
job1 := testJob(jobID)
resp, _, err := client.Jobs().Register(job1, nil)
require.NoError(err)
if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
t.Fatalf("status code non zero saw %d", code)
}
must.NoError(t, err)
code = waitForSuccess(ui, client, fullId, t, resp.EvalID)
must.Zero(t, code)
// get an alloc id
allocId1 := ""
if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil {
@ -85,11 +87,15 @@ func TestAllocRestartCommand_Fails(t *testing.T) {
allocId1 = allocs[0].ID
}
}
require.NotEmpty(allocId1, "unable to find allocation")
must.NotEq(t, "", allocId1)
// Fails on not found task
require.Equal(cmd.Run([]string{"-address=" + url, allocId1, "fooooobarrr"}), 1)
require.Contains(ui.ErrorWriter.String(), "Could not find task named")
code = cmd.Run([]string{"-address=" + url, allocId1, "fooooobarrr"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Could not find task named")
ui.ErrorWriter.Reset()
}
@ -97,26 +103,10 @@ func TestAllocRestartCommand_Run(t *testing.T) {
ci.Parallel(t)
srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
require := require.New(t)
defer stopTestAgent(srv)
// Wait for a node to be ready
testutil.WaitForResult(func() (bool, error) {
nodes, _, err := client.Nodes().List(nil)
if err != nil {
return false, err
}
for _, node := range nodes {
if _, ok := node.Drivers["mock_driver"]; ok &&
node.Status == structs.NodeStatusReady {
return true, nil
}
}
return false, fmt.Errorf("no ready nodes")
}, func(err error) {
t.Fatalf("err: %v", err)
})
waitForNodes(t, client)
ui := cli.NewMockUi()
cmd := &AllocRestartCommand{Meta: Meta{Ui: ui}}
@ -124,34 +114,25 @@ func TestAllocRestartCommand_Run(t *testing.T) {
jobID := "job1_sfx"
job1 := testJob(jobID)
resp, _, err := client.Jobs().Register(job1, nil)
require.NoError(err)
if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
t.Fatalf("status code non zero saw %d", code)
}
must.NoError(t, err)
code := waitForSuccess(ui, client, fullId, t, resp.EvalID)
must.Zero(t, code)
// get an alloc id
allocId1 := ""
allocID := ""
if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil {
if len(allocs) > 0 {
allocId1 = allocs[0].ID
allocID = allocs[0].ID
}
}
require.NotEmpty(allocId1, "unable to find allocation")
must.NotEq(t, "", allocID)
// Wait for alloc to be running
testutil.WaitForResult(func() (bool, error) {
alloc, _, err := client.Allocations().Info(allocId1, nil)
if err != nil {
return false, err
}
if alloc.ClientStatus == api.AllocClientStatusRunning {
return true, nil
}
return false, fmt.Errorf("alloc is not running, is: %s", alloc.ClientStatus)
}, func(err error) {
t.Fatalf("err: %v", err)
})
waitForAllocRunning(t, client, allocID)
require.Equal(cmd.Run([]string{"-address=" + url, allocId1}), 0, "expected successful exit code")
code = cmd.Run([]string{"-address=" + url, allocID})
must.Zero(t, code)
ui.OutputWriter.Reset()
}
@ -159,10 +140,8 @@ func TestAllocRestartCommand_Run(t *testing.T) {
func TestAllocRestartCommand_AutocompleteArgs(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
srv, _, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocRestartCommand{Meta: Meta{Ui: ui, flagAddress: url}}
@ -170,13 +149,13 @@ func TestAllocRestartCommand_AutocompleteArgs(t *testing.T) {
// Create a fake alloc
state := srv.Agent.Server().State()
a := mock.Alloc()
assert.Nil(state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
prefix := a.ID[:5]
args := complete.Args{Last: prefix}
predictor := cmd.AutocompleteArgs()
res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(a.ID, res[0])
must.Len(t, 1, res)
must.Eq(t, a.ID, res[0])
}

View File

@ -1,18 +1,14 @@
package command
import (
"fmt"
"testing"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/shoenig/test/must"
)
func TestAllocSignalCommand_Implements(t *testing.T) {
@ -23,47 +19,62 @@ func TestAllocSignalCommand_Implements(t *testing.T) {
func TestAllocSignalCommand_Fails(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
require := require.New(t)
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocSignalCommand{Meta: Meta{Ui: ui}}
// Fails on lack of alloc ID
require.Equal(1, cmd.Run([]string{}))
require.Contains(ui.ErrorWriter.String(), "This command takes up to two arguments")
code := cmd.Run([]string{})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, "This command takes up to two arguments")
ui.ErrorWriter.Reset()
// Fails on misuse
require.Equal(1, cmd.Run([]string{"some", "bad", "args"}))
require.Contains(ui.ErrorWriter.String(), "This command takes up to two arguments")
code = cmd.Run([]string{"some", "bad", "args"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "This command takes up to two arguments")
ui.ErrorWriter.Reset()
// Fails on connection failure
require.Equal(1, cmd.Run([]string{"-address=nope", "foobar"}))
require.Contains(ui.ErrorWriter.String(), "Error querying allocation")
code = cmd.Run([]string{"-address=nope", "foobar"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Error querying allocation")
ui.ErrorWriter.Reset()
// Fails on missing alloc
code := cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"})
require.Equal(1, code)
require.Contains(ui.ErrorWriter.String(), "No allocation(s) with prefix or id")
code = cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Fail on identifier with too few characters
require.Equal(1, cmd.Run([]string{"-address=" + url, "2"}))
require.Contains(ui.ErrorWriter.String(), "must contain at least two characters.")
code = cmd.Run([]string{"-address=" + url, "2"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "must contain at least two characters.")
ui.ErrorWriter.Reset()
}
func TestAllocSignalCommand_AutocompleteArgs(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
srv, _, url := testServer(t, true, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AllocSignalCommand{Meta: Meta{Ui: ui, flagAddress: url}}
@ -71,7 +82,7 @@ func TestAllocSignalCommand_AutocompleteArgs(t *testing.T) {
// Create a fake alloc
state := srv.Agent.Server().State()
a := mock.Alloc()
assert.Nil(state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
prefix := a.ID[:5]
args := complete.Args{All: []string{"signal", prefix}, Last: prefix}
@ -79,34 +90,18 @@ func TestAllocSignalCommand_AutocompleteArgs(t *testing.T) {
// Match Allocs
res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(a.ID, res[0])
must.Len(t, 1, res)
must.Eq(t, a.ID, res[0])
}
func TestAllocSignalCommand_Run(t *testing.T) {
ci.Parallel(t)
srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
require := require.New(t)
defer stopTestAgent(srv)
// Wait for a node to be ready
testutil.WaitForResult(func() (bool, error) {
nodes, _, err := client.Nodes().List(nil)
if err != nil {
return false, err
}
for _, node := range nodes {
if _, ok := node.Drivers["mock_driver"]; ok &&
node.Status == structs.NodeStatusReady {
return true, nil
}
}
return false, fmt.Errorf("no ready nodes")
}, func(err error) {
t.Fatalf("err: %v", err)
})
waitForNodes(t, client)
ui := cli.NewMockUi()
cmd := &AllocSignalCommand{Meta: Meta{Ui: ui}}
@ -114,34 +109,25 @@ func TestAllocSignalCommand_Run(t *testing.T) {
jobID := "job1_sfx"
job1 := testJob(jobID)
resp, _, err := client.Jobs().Register(job1, nil)
require.NoError(err)
if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
t.Fatalf("status code non zero saw %d", code)
}
must.NoError(t, err)
code := waitForSuccess(ui, client, fullId, t, resp.EvalID)
must.Zero(t, code)
// get an alloc id
allocId1 := ""
allocID := ""
if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil {
if len(allocs) > 0 {
allocId1 = allocs[0].ID
allocID = allocs[0].ID
}
}
require.NotEmpty(allocId1, "unable to find allocation")
must.NotEq(t, "", allocID)
// Wait for alloc to be running
testutil.WaitForResult(func() (bool, error) {
alloc, _, err := client.Allocations().Info(allocId1, nil)
if err != nil {
return false, err
}
if alloc.ClientStatus == api.AllocClientStatusRunning {
return true, nil
}
return false, fmt.Errorf("alloc is not running, is: %s", alloc.ClientStatus)
}, func(err error) {
t.Fatalf("err: %v", err)
})
waitForAllocRunning(t, client, allocID)
require.Equal(cmd.Run([]string{"-address=" + url, allocId1}), 0, "expected successful exit code")
code = cmd.Run([]string{"-address=" + url, allocID})
must.Zero(t, code)
ui.OutputWriter.Reset()
}

View File

@ -3,7 +3,6 @@ package command
import (
"fmt"
"regexp"
"strings"
"testing"
"time"
@ -15,7 +14,6 @@ import (
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/shoenig/test/must"
"github.com/stretchr/testify/require"
)
func TestAllocStatusCommand_Implements(t *testing.T) {
@ -32,61 +30,61 @@ func TestAllocStatusCommand_Fails(t *testing.T) {
cmd := &AllocStatusCommand{Meta: Meta{Ui: ui}}
// Fails on misuse
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
t.Fatalf("expected help output, got: %s", out)
}
code := cmd.Run([]string{"some", "bad", "args"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, commandErrorText(cmd))
ui.ErrorWriter.Reset()
// Fails on connection failure
if code := cmd.Run([]string{"-address=nope", "foobar"}); code != 1 {
t.Fatalf("expected exit code 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying allocation") {
t.Fatalf("expected failed query error, got: %s", out)
}
code = cmd.Run([]string{"-address=nope", "foobar"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Error querying allocation")
ui.ErrorWriter.Reset()
// Fails on missing alloc
if code := cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No allocation(s) with prefix or id") {
t.Fatalf("expected not found error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Fail on identifier with too few characters
if code := cmd.Run([]string{"-address=" + url, "2"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "must contain at least two characters.") {
t.Fatalf("expected too few characters error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "2"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "must contain at least two characters.")
ui.ErrorWriter.Reset()
// Identifiers with uneven length should produce a query result
if code := cmd.Run([]string{"-address=" + url, "123"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No allocation(s) with prefix or id") {
t.Fatalf("expected not found error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "123"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Failed on both -json and -t options are specified
if code := cmd.Run([]string{"-address=" + url, "-json", "-t", "{{.ID}}"}); code != 1 {
t.Fatalf("expected exit 1, got: %d", code)
}
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Both json and template formatting are not allowed") {
t.Fatalf("expected getting formatter error, got: %s", out)
}
code = cmd.Run([]string{"-address=" + url, "-json", "-t", "{{.ID}}"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Both json and template formatting are not allowed")
}
func TestAllocStatusCommand_LifecycleInfo(t *testing.T) {
ci.Parallel(t)
srv, client, url := testServer(t, true, nil)
defer stopTestAgent(srv)
@ -122,16 +120,15 @@ func TestAllocStatusCommand_LifecycleInfo(t *testing.T) {
"prestart_sidecar": {State: "running"},
}
require.Nil(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
code := cmd.Run([]string{"-address=" + url, a.ID})
must.Zero(t, code)
if code := cmd.Run([]string{"-address=" + url, a.ID}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
out := ui.OutputWriter.String()
require.Contains(t, out, `Task "init_task" (prestart) is "running"`)
require.Contains(t, out, `Task "prestart_sidecar" (prestart sidecar) is "running"`)
require.Contains(t, out, `Task "web" is "pending"`)
must.StrContains(t, out, `Task "init_task" (prestart) is "running"`)
must.StrContains(t, out, `Task "prestart_sidecar" (prestart sidecar) is "running"`)
must.StrContains(t, out, `Task "web" is "pending"`)
}
func TestAllocStatusCommand_Run(t *testing.T) {
@ -147,73 +144,56 @@ func TestAllocStatusCommand_Run(t *testing.T) {
jobID := "job1_sfx"
job1 := testJob(jobID)
resp, _, err := client.Jobs().Register(job1, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
t.Fatalf("status code non zero saw %d", code)
}
must.NoError(t, err)
code := waitForSuccess(ui, client, fullId, t, resp.EvalID)
must.Zero(t, code)
// get an alloc id
allocId1 := ""
allocID := ""
nodeName := ""
if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil {
if len(allocs) > 0 {
allocId1 = allocs[0].ID
allocID = allocs[0].ID
nodeName = allocs[0].NodeName
}
}
if allocId1 == "" {
t.Fatal("unable to find an allocation")
}
must.NotEq(t, "", allocID)
code = cmd.Run([]string{"-address=" + url, allocID})
must.Zero(t, code)
if code := cmd.Run([]string{"-address=" + url, allocId1}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
out := ui.OutputWriter.String()
if !strings.Contains(out, "Created") {
t.Fatalf("expected to have 'Created' but saw: %s", out)
}
if !strings.Contains(out, "Modified") {
t.Fatalf("expected to have 'Modified' but saw: %s", out)
}
must.StrContains(t, out, "Created")
must.StrContains(t, out, "Modified")
nodeNameRegexpStr := fmt.Sprintf(`\nNode Name\s+= %s\n`, regexp.QuoteMeta(nodeName))
require.Regexp(t, regexp.MustCompile(nodeNameRegexpStr), out)
must.RegexMatch(t, regexp.MustCompile(nodeNameRegexpStr), out)
ui.OutputWriter.Reset()
if code := cmd.Run([]string{"-address=" + url, "-verbose", allocId1}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
code = cmd.Run([]string{"-address=" + url, "-verbose", allocID})
must.Zero(t, code)
out = ui.OutputWriter.String()
if !strings.Contains(out, allocId1) {
t.Fatal("expected to find alloc id in output")
}
if !strings.Contains(out, "Created") {
t.Fatalf("expected to have 'Created' but saw: %s", out)
}
must.StrContains(t, out, allocID)
must.StrContains(t, out, "Created")
ui.OutputWriter.Reset()
// Try the query with an even prefix that includes the hyphen
if code := cmd.Run([]string{"-address=" + url, allocId1[:13]}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
code = cmd.Run([]string{"-address=" + url, allocID[:13]})
must.Zero(t, code)
out = ui.OutputWriter.String()
if !strings.Contains(out, "Created") {
t.Fatalf("expected to have 'Created' but saw: %s", out)
}
must.StrContains(t, out, "Created")
ui.OutputWriter.Reset()
if code := cmd.Run([]string{"-address=" + url, "-verbose", allocId1}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
out = ui.OutputWriter.String()
if !strings.Contains(out, allocId1) {
t.Fatal("expected to find alloc id in output")
}
ui.OutputWriter.Reset()
code = cmd.Run([]string{"-address=" + url, "-verbose", allocID})
must.Zero(t, code)
out = ui.OutputWriter.String()
must.StrContains(t, out, allocID)
}
func TestAllocStatusCommand_RescheduleInfo(t *testing.T) {
@ -240,14 +220,14 @@ func TestAllocStatusCommand_RescheduleInfo(t *testing.T) {
},
},
}
require.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
if code := cmd.Run([]string{"-address=" + url, a.ID}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
out := ui.OutputWriter.String()
require.Contains(t, out, "Replacement Alloc ID")
require.Regexp(t, regexp.MustCompile(".*Reschedule Attempts\\s*=\\s*1/2"), out)
must.StrContains(t, out, "Replacement Alloc ID")
must.RegexMatch(t, regexp.MustCompile(".*Reschedule Attempts\\s*=\\s*1/2"), out)
}
func TestAllocStatusCommand_ScoreMetrics(t *testing.T) {
@ -283,19 +263,19 @@ func TestAllocStatusCommand_ScoreMetrics(t *testing.T) {
},
},
}
require.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{a}))
code := cmd.Run([]string{"-address=" + url, "-verbose", a.ID})
must.Zero(t, code)
if code := cmd.Run([]string{"-address=" + url, "-verbose", a.ID}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
out := ui.OutputWriter.String()
require.Contains(t, out, "Placement Metrics")
require.Contains(t, out, mockNode1.ID)
require.Contains(t, out, mockNode2.ID)
must.StrContains(t, out, "Placement Metrics")
must.StrContains(t, out, mockNode1.ID)
must.StrContains(t, out, mockNode2.ID)
// assert we sort headers alphabetically
require.Contains(t, out, "binpack node-affinity")
require.Contains(t, out, "final score")
must.StrContains(t, out, "binpack node-affinity")
must.StrContains(t, out, "final score")
}
func TestAllocStatusCommand_AutocompleteArgs(t *testing.T) {
@ -372,18 +352,18 @@ func TestAllocStatusCommand_HostVolumes(t *testing.T) {
},
}
summary := mock.JobSummary(alloc.JobID)
require.NoError(t, state.UpsertJobSummary(1004, summary))
require.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1005, []*structs.Allocation{alloc}))
must.NoError(t, state.UpsertJobSummary(1004, summary))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1005, []*structs.Allocation{alloc}))
ui := cli.NewMockUi()
cmd := &AllocStatusCommand{Meta: Meta{Ui: ui}}
if code := cmd.Run([]string{"-address=" + url, "-verbose", alloc.ID}); code != 0 {
t.Fatalf("expected exit 0, got: %d", code)
}
code := cmd.Run([]string{"-address=" + url, "-verbose", alloc.ID})
must.Zero(t, code)
out := ui.OutputWriter.String()
require.Contains(t, out, "Host Volumes")
require.Contains(t, out, fmt.Sprintf("%s true", vol0))
require.NotContains(t, out, "CSI Volumes")
must.StrContains(t, out, "Host Volumes")
must.StrContains(t, out, fmt.Sprintf("%s true", vol0))
must.StrNotContains(t, out, "CSI Volumes")
}
func TestAllocStatusCommand_CSIVolumes(t *testing.T) {
@ -404,7 +384,7 @@ func TestAllocStatusCommand_CSIVolumes(t *testing.T) {
},
}
err := state.UpsertNode(structs.MsgTypeTestSetup, 1001, node)
require.NoError(t, err)
must.NoError(t, err)
vols := []*structs.CSIVolume{{
ID: vol0,
@ -417,7 +397,7 @@ func TestAllocStatusCommand_CSIVolumes(t *testing.T) {
}},
}}
err = state.UpsertCSIVolume(1002, vols)
require.NoError(t, err)
must.NoError(t, err)
// Upsert the job and alloc
alloc := mock.Alloc()
@ -448,8 +428,8 @@ func TestAllocStatusCommand_CSIVolumes(t *testing.T) {
},
}
summary := mock.JobSummary(alloc.JobID)
require.NoError(t, state.UpsertJobSummary(1004, summary))
require.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1005, []*structs.Allocation{alloc}))
must.NoError(t, state.UpsertJobSummary(1004, summary))
must.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1005, []*structs.Allocation{alloc}))
ui := cli.NewMockUi()
cmd := &AllocStatusCommand{Meta: Meta{Ui: ui}}
@ -457,7 +437,7 @@ func TestAllocStatusCommand_CSIVolumes(t *testing.T) {
t.Fatalf("expected exit 0, got: %d", code)
}
out := ui.OutputWriter.String()
require.Contains(t, out, "CSI Volumes")
require.Contains(t, out, fmt.Sprintf("%s minnie", vol0))
require.NotContains(t, out, "Host Volumes")
must.StrContains(t, out, "CSI Volumes")
must.StrContains(t, out, fmt.Sprintf("%s minnie", vol0))
must.StrNotContains(t, out, "Host Volumes")
}

View File

@ -1,15 +1,11 @@
package command
import (
"fmt"
"testing"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/require"
"github.com/shoenig/test/must"
)
func TestAllocStopCommand_Implements(t *testing.T) {
@ -19,62 +15,61 @@ func TestAllocStopCommand_Implements(t *testing.T) {
func TestAllocStop_Fails(t *testing.T) {
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
require := require.New(t)
ui := cli.NewMockUi()
cmd := &AllocStopCommand{Meta: Meta{Ui: ui}}
// Fails on misuse
require.Equal(cmd.Run([]string{"some", "garbage", "args"}), 1, "Expected failure")
require.Contains(ui.ErrorWriter.String(), commandErrorText(cmd), "Expected help output")
code := cmd.Run([]string{"some", "garbage", "args"})
must.One(t, code)
out := ui.ErrorWriter.String()
must.StrContains(t, out, commandErrorText(cmd))
ui.ErrorWriter.Reset()
// Fails on connection failure
require.Equal(cmd.Run([]string{"-address=nope", "foobar"}), 1, "expected failure")
require.Contains(ui.ErrorWriter.String(), "Error querying allocation")
code = cmd.Run([]string{"-address=nope", "foobar"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "Error querying allocation")
ui.ErrorWriter.Reset()
// Fails on missing alloc
require.Equal(cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"}), 1)
require.Contains(ui.ErrorWriter.String(), "No allocation(s) with prefix or id")
code = cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
// Fail on identifier with too few characters
require.Equal(cmd.Run([]string{"-address=" + url, "2"}), 1)
require.Contains(ui.ErrorWriter.String(), "must contain at least two characters")
code = cmd.Run([]string{"-address=" + url, "2"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "must contain at least two characters")
ui.ErrorWriter.Reset()
// Identifiers with uneven length should produce a query result
require.Equal(cmd.Run([]string{"-address=" + url, "123"}), 1)
require.Contains(ui.ErrorWriter.String(), "No allocation(s) with prefix or id")
ui.ErrorWriter.Reset()
code = cmd.Run([]string{"-address=" + url, "123"})
must.One(t, code)
out = ui.ErrorWriter.String()
must.StrContains(t, out, "No allocation(s) with prefix or id")
}
func TestAllocStop_Run(t *testing.T) {
ci.Parallel(t)
srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
require := require.New(t)
defer stopTestAgent(srv)
// Wait for a node to be ready
testutil.WaitForResult(func() (bool, error) {
nodes, _, err := client.Nodes().List(nil)
if err != nil {
return false, err
}
for _, node := range nodes {
if _, ok := node.Drivers["mock_driver"]; ok &&
node.Status == structs.NodeStatusReady {
return true, nil
}
}
return false, fmt.Errorf("no ready nodes")
}, func(err error) {
t.Fatalf("err: %v", err)
})
waitForNodes(t, client)
ui := cli.NewMockUi()
cmd := &AllocStopCommand{Meta: Meta{Ui: ui}}
@ -82,34 +77,23 @@ func TestAllocStop_Run(t *testing.T) {
jobID := "job1_sfx"
job1 := testJob(jobID)
resp, _, err := client.Jobs().Register(job1, nil)
require.NoError(err)
if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
t.Fatalf("status code non zero saw %d", code)
}
must.NoError(t, err)
code := waitForSuccess(ui, client, fullId, t, resp.EvalID)
must.Zero(t, code)
// get an alloc id
allocId1 := ""
allocID := ""
if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil {
if len(allocs) > 0 {
allocId1 = allocs[0].ID
allocID = allocs[0].ID
}
}
require.NotEmpty(allocId1, "unable to find allocation")
must.NotEq(t, "", allocID)
// Wait for alloc to be running
testutil.WaitForResult(func() (bool, error) {
alloc, _, err := client.Allocations().Info(allocId1, nil)
if err != nil {
return false, err
}
if alloc.ClientStatus == api.AllocClientStatusRunning {
return true, nil
}
return false, fmt.Errorf("alloc is not running, is: %s", alloc.ClientStatus)
}, func(err error) {
t.Fatalf("err: %v", err)
})
waitForAllocRunning(t, client, allocID)
require.Equal(cmd.Run([]string{"-address=" + url, allocId1}), 0, "expected successful exit code")
ui.OutputWriter.Reset()
code = cmd.Run([]string{"-address=" + url, allocID})
must.Zero(t, code)
}

View File

@ -6,25 +6,22 @@ import (
"github.com/hashicorp/nomad/ci"
"github.com/mitchellh/cli"
"github.com/shoenig/test/must"
)
func TestAgentCheckCommand_ServerHealth(t *testing.T) {
ci.Parallel(t)
srv, _, url := testServer(t, false, nil)
defer srv.Shutdown()
defer stopTestAgent(srv)
ui := cli.NewMockUi()
cmd := &AgentCheckCommand{Meta: Meta{Ui: ui}}
address := fmt.Sprintf("-address=%s", url)
code := cmd.Run([]string{address})
if code != HealthPass {
t.Fatalf("expected exit: %v, actual: %d", HealthPass, code)
}
must.Eq(t, HealthPass, code)
minPeers := fmt.Sprintf("-min-peers=%v", 3)
code = cmd.Run([]string{address, minPeers})
if code != HealthCritical {
t.Fatalf("expected exitcode: %v, actual: %v", HealthCritical, code)
}
must.Eq(t, HealthCritical, code)
}

View File

@ -1,12 +1,13 @@
package command
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/hashicorp/nomad/ci"
"github.com/mitchellh/cli"
"github.com/shoenig/test/must"
)
func TestConfigValidateCommand_FailWithEmptyDir(t *testing.T) {
@ -18,9 +19,7 @@ func TestConfigValidateCommand_FailWithEmptyDir(t *testing.T) {
args := []string{fh}
code := cmd.Run(args)
if code != 1 {
t.Fatalf("expected exit 1, actual: %d", code)
}
must.One(t, code)
}
func TestConfigValidateCommand_SucceedWithMinimalConfigFile(t *testing.T) {
@ -28,22 +27,18 @@ func TestConfigValidateCommand_SucceedWithMinimalConfigFile(t *testing.T) {
fh := t.TempDir()
fp := filepath.Join(fh, "config.hcl")
err := ioutil.WriteFile(fp, []byte(`data_dir="/"
err := os.WriteFile(fp, []byte(`data_dir="/"
client {
enabled = true
}`), 0644)
if err != nil {
t.Fatalf("err: %s", err)
}
must.NoError(t, err)
ui := cli.NewMockUi()
cmd := &ConfigValidateCommand{Meta: Meta{Ui: ui}}
args := []string{fh}
code := cmd.Run(args)
if code != 0 {
t.Fatalf("expected exit 0, actual: %d", code)
}
must.Zero(t, code)
}
func TestConfigValidateCommand_FailOnParseBadConfigFile(t *testing.T) {
@ -51,19 +46,15 @@ func TestConfigValidateCommand_FailOnParseBadConfigFile(t *testing.T) {
fh := t.TempDir()
fp := filepath.Join(fh, "config.hcl")
err := ioutil.WriteFile(fp, []byte(`a: b`), 0644)
if err != nil {
t.Fatalf("err: %s", err)
}
err := os.WriteFile(fp, []byte(`a: b`), 0644)
must.NoError(t, err)
ui := cli.NewMockUi()
cmd := &ConfigValidateCommand{Meta: Meta{Ui: ui}}
args := []string{fh}
code := cmd.Run(args)
if code != 1 {
t.Fatalf("expected exit 1, actual: %d", code)
}
must.One(t, code)
}
func TestConfigValidateCommand_FailOnValidateParsableConfigFile(t *testing.T) {
@ -71,20 +62,16 @@ func TestConfigValidateCommand_FailOnValidateParsableConfigFile(t *testing.T) {
fh := t.TempDir()
fp := filepath.Join(fh, "config.hcl")
err := ioutil.WriteFile(fp, []byte(`data_dir="../"
err := os.WriteFile(fp, []byte(`data_dir="../"
client {
enabled = true
}`), 0644)
if err != nil {
t.Fatalf("err: %s", err)
}
must.NoError(t, err)
ui := cli.NewMockUi()
cmd := &ConfigValidateCommand{Meta: Meta{Ui: ui}}
args := []string{fh}
code := cmd.Run(args)
if code != 1 {
t.Fatalf("expected exit 1, actual: %d", code)
}
must.One(t, code)
}

View File

@ -129,6 +129,21 @@ func waitForNodes(t *testing.T, client *api.Client) {
})
}
func waitForAllocRunning(t *testing.T, client *api.Client, allocID string) {
testutil.WaitForResult(func() (bool, error) {
alloc, _, err := client.Allocations().Info(allocID, nil)
if err != nil {
return false, err
}
if alloc.ClientStatus == api.AllocClientStatusRunning {
return true, nil
}
return false, fmt.Errorf("alloc status: %s", alloc.ClientStatus)
}, func(err error) {
t.Fatalf("timed out waiting for alloc to be running: %v", err)
})
}
func stopTestAgent(a *agent.TestAgent) {
_ = a.Shutdown()
}