Merge pull request #3026 from hashicorp/f-autocomplete-job-eval-node

Add autocomplete for nodes, evals, and jobs
This commit is contained in:
Chelsea Komlo 2017-08-15 17:12:17 -04:00 committed by GitHub
commit 1ff04de2a7
7 changed files with 204 additions and 1 deletions

View file

@ -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])
}

View file

@ -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 {

View file

@ -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])
}

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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])
}