diff --git a/command/alloc_status_test.go b/command/alloc_status_test.go index 1a5a0e600..2b9e1473a 100644 --- a/command/alloc_status_test.go +++ b/command/alloc_status_test.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/testutil" "github.com/mitchellh/cli" + "github.com/posener/complete" + "github.com/stretchr/testify/assert" ) func TestAllocStatusCommand_Implements(t *testing.T) { @@ -142,3 +144,43 @@ func TestAllocStatusCommand_Run(t *testing.T) { } ui.OutputWriter.Reset() } + +func TestAllocStatusCommand_AutocompleteArgs(t *testing.T) { + assert := assert.New(t) + t.Parallel() + + srv, client, url := testServer(t, true, nil) + defer srv.Shutdown() + + ui := new(cli.MockUi) + cmd := &AllocStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}} + + 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) + } + + // get an alloc id + allocID := "" + if allocs, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil { + if len(allocs) > 0 { + allocID = allocs[0].ID + } + } + if allocID == "" { + t.Fatal("unable to find an allocation") + } + + prefix := allocID[:len(allocID)-5] + args := complete.Args{Last: prefix} + predictor := cmd.AutocompleteArgs() + + res := predictor.Predict(args) + assert.Equal(1, len(res)) + assert.Equal(allocID, res[0]) +} diff --git a/command/eval_status.go b/command/eval_status.go index f2891b316..78a96e7f6 100644 --- a/command/eval_status.go +++ b/command/eval_status.go @@ -6,6 +6,8 @@ import ( "strings" "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/api/contexts" + "github.com/posener/complete" ) type EvalStatusCommand struct { @@ -218,6 +220,21 @@ func (c *EvalStatusCommand) Run(args []string) int { return 0 } +func (c *EvalStatusCommand) AutocompleteFlags() complete.Flags { + return nil +} + +func (c *EvalStatusCommand) AutocompleteArgs() complete.Predictor { + client, _ := c.Meta.Client() + return complete.PredictFunc(func(a complete.Args) []string { + resp, err := client.Search().PrefixSearch(a.Last, contexts.Evals) + if err != nil { + return []string{} + } + return resp.Matches[contexts.Evals] + }) +} + func sortedTaskGroupFromMetrics(groups map[string]*api.AllocationMetric) []string { tgs := make([]string, 0, len(groups)) for tg, _ := range groups { diff --git a/command/eval_status_test.go b/command/eval_status_test.go index 4f0ea9824..90c4bd0f9 100644 --- a/command/eval_status_test.go +++ b/command/eval_status_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/mitchellh/cli" + "github.com/posener/complete" + "github.com/stretchr/testify/assert" ) func TestEvalStatusCommand_Implements(t *testing.T) { @@ -56,3 +58,43 @@ func TestEvalStatusCommand_Fails(t *testing.T) { } } + +func TestEvalStatusCommand_AutocompleteArgs(t *testing.T) { + assert := assert.New(t) + t.Parallel() + + srv, client, url := testServer(t, true, nil) + defer srv.Shutdown() + + ui := new(cli.MockUi) + cmd := &EvalStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}} + + 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) + } + + // get an eval id + evalID := "" + if evals, _, err := client.Jobs().Evaluations(jobID, nil); err == nil { + if len(evals) > 0 { + evalID = evals[0].ID + } + } + if evalID == "" { + t.Fatal("unable to find an evaluation") + } + + prefix := evalID[:len(evalID)-5] + args := complete.Args{Last: prefix} + predictor := cmd.AutocompleteArgs() + + res := predictor.Predict(args) + assert.Equal(1, len(res)) + assert.Equal(evalID, res[0]) +} diff --git a/command/job_status.go b/command/job_status.go index 82a977a49..e543b0860 100644 --- a/command/job_status.go +++ b/command/job_status.go @@ -7,7 +7,9 @@ import ( "time" "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/api/contexts" "github.com/hashicorp/nomad/nomad/structs" + "github.com/posener/complete" ) const ( @@ -525,6 +527,21 @@ func (c *JobStatusCommand) outputFailedPlacements(failedEval *api.Evaluation) { } } +func (c *JobStatusCommand) AutocompleteFlags() complete.Flags { + return nil +} + +func (c *JobStatusCommand) AutocompleteArgs() complete.Predictor { + client, _ := c.Meta.Client() + return complete.PredictFunc(func(a complete.Args) []string { + resp, err := client.Search().PrefixSearch(a.Last, contexts.Jobs) + if err != nil { + return []string{} + } + return resp.Matches[contexts.Jobs] + }) +} + // list general information about a list of jobs func createStatusListOutput(jobs []*api.JobListStub) string { out := make([]string, len(jobs)+1) diff --git a/command/job_status_test.go b/command/job_status_test.go index c51aae421..bb4bf195f 100644 --- a/command/job_status_test.go +++ b/command/job_status_test.go @@ -6,6 +6,8 @@ import ( "github.com/hashicorp/nomad/api" "github.com/mitchellh/cli" + "github.com/posener/complete" + "github.com/stretchr/testify/assert" ) func TestJobStatusCommand_Implements(t *testing.T) { @@ -188,6 +190,35 @@ func TestJobStatusCommand_Fails(t *testing.T) { } } +func TestJobStatusCommand_AutocompleteArgs(t *testing.T) { + assert := assert.New(t) + t.Parallel() + + srv, client, url := testServer(t, true, nil) + defer srv.Shutdown() + + ui := new(cli.MockUi) + cmd := &JobStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}} + + 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) + } + + prefix := jobID[:len(jobID)-5] + args := complete.Args{Last: prefix} + predictor := cmd.AutocompleteArgs() + + res := predictor.Predict(args) + assert.Equal(1, len(res)) + assert.Equal(jobID, res[0]) +} + func waitForSuccess(ui cli.Ui, client *api.Client, length int, t *testing.T, evalId string) int { mon := newMonitor(ui, client, length) monErr := mon.monitor(evalId, false) diff --git a/command/node_status.go b/command/node_status.go index 67983533c..23e034fb1 100644 --- a/command/node_status.go +++ b/command/node_status.go @@ -7,10 +7,12 @@ import ( "strings" "time" - "github.com/dustin/go-humanize" + humanize "github.com/dustin/go-humanize" "github.com/mitchellh/colorstring" + "github.com/posener/complete" "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/api/contexts" "github.com/hashicorp/nomad/helper" ) @@ -436,6 +438,21 @@ func (c *NodeStatusCommand) printDiskStats(hostStats *api.HostStats) { } } +func (c *NodeStatusCommand) AutocompleteFlags() complete.Flags { + return nil +} + +func (c *NodeStatusCommand) AutocompleteArgs() complete.Predictor { + client, _ := c.Meta.Client() + return complete.PredictFunc(func(a complete.Args) []string { + resp, err := client.Search().PrefixSearch(a.Last, contexts.Nodes) + if err != nil { + return []string{} + } + return resp.Matches[contexts.Nodes] + }) +} + // getRunningAllocs returns a slice of allocation id's running on the node func getRunningAllocs(client *api.Client, nodeID string) ([]*api.Allocation, error) { var allocs []*api.Allocation diff --git a/command/node_status_test.go b/command/node_status_test.go index 9900f87cf..bb1e5ac9c 100644 --- a/command/node_status_test.go +++ b/command/node_status_test.go @@ -8,6 +8,8 @@ import ( "github.com/hashicorp/nomad/command/agent" "github.com/hashicorp/nomad/testutil" "github.com/mitchellh/cli" + "github.com/posener/complete" + "github.com/stretchr/testify/assert" ) func TestNodeStatusCommand_Implements(t *testing.T) { @@ -213,3 +215,38 @@ func TestNodeStatusCommand_Fails(t *testing.T) { t.Fatalf("expected getting formatter error, got: %s", out) } } + +func TestNodeStatusCommand_AutocompleteArgs(t *testing.T) { + assert := assert.New(t) + t.Parallel() + + srv, client, url := testServer(t, true, nil) + defer srv.Shutdown() + + // 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) + }) + + ui := new(cli.MockUi) + cmd := &NodeStatusCommand{Meta: Meta{Ui: ui, flagAddress: url}} + + prefix := nodeID[:len(nodeID)-5] + args := complete.Args{Last: prefix} + predictor := cmd.AutocompleteArgs() + + res := predictor.Predict(args) + assert.Equal(1, len(res)) + assert.Equal(nodeID, res[0]) +}