Skip to content

Commit

Permalink
auto-complete for recommendations CLI, plus OSS components of recomme…
Browse files Browse the repository at this point in the history
…ndations prefix search
  • Loading branch information
cgbaker committed Nov 11, 2020
1 parent 1bd7ce7 commit e77fbe9
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 49 deletions.
21 changes: 11 additions & 10 deletions api/contexts/contexts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ package contexts
type Context string

const (
Allocs Context = "allocs"
Deployments Context = "deployment"
Evals Context = "evals"
Jobs Context = "jobs"
Nodes Context = "nodes"
Namespaces Context = "namespaces"
Quotas Context = "quotas"
Plugins Context = "plugins"
Volumes Context = "volumes"
All Context = "all"
Allocs Context = "allocs"
Deployments Context = "deployment"
Evals Context = "evals"
Jobs Context = "jobs"
Nodes Context = "nodes"
Namespaces Context = "namespaces"
Quotas Context = "quotas"
Recommendations Context = "recommendations"
Plugins Context = "plugins"
Volumes Context = "volumes"
All Context = "all"
)
17 changes: 17 additions & 0 deletions command/recommendation_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"strings"

"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/api/contexts"

"github.com/mitchellh/cli"
"github.com/posener/complete"
)
Expand Down Expand Up @@ -60,6 +62,21 @@ func (r *RecommendationApplyCommand) AutocompleteFlags() complete.Flags {
})
}

func (r *RecommendationApplyCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictFunc(func(a complete.Args) []string {
client, err := r.Meta.Client()
if err != nil {
return nil
}

resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Recommendations, nil)
if err != nil {
return []string{}
}
return resp.Matches[contexts.Recommendations]
})
}

// Name returns the name of this command.
func (r *RecommendationApplyCommand) Name() string { return "recommendation apply" }

Expand Down
45 changes: 45 additions & 0 deletions command/recommendation_apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -83,3 +85,46 @@ func TestRecommendationApplyCommand_Run(t *testing.T) {
require.NoError(err)
require.Equal(1, *jobResp.TaskGroups[0].Tasks[0].Resources.CPU)
}

func TestRecommendationApplyCommand_AutocompleteArgs(t *testing.T) {
assert := assert.New(t)
t.Parallel()

srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()

// Register a test job to write a recommendation against.
ui := cli.NewMockUi()
testJob := testJob("recommendation_list")
regResp, _, err := client.Jobs().Register(testJob, nil)
require.NoError(t, err)
registerCode := waitForSuccess(ui, client, fullId, t, regResp.EvalID)
require.Equal(t, 0, registerCode)

// Write a recommendation.
rec := &api.Recommendation{
JobID: *testJob.ID,
Group: *testJob.TaskGroups[0].Name,
Task: testJob.TaskGroups[0].Tasks[0].Name,
Resource: "CPU",
Value: 1050,
Meta: map[string]interface{}{"test-meta-entry": "test-meta-value"},
Stats: map[string]float64{"p13": 1.13},
}
rec, _, err = client.Recommendations().Upsert(rec, nil)
if srv.Enterprise {
require.NoError(t, err)
} else {
require.Error(t, err, "Nomad Enterprise only endpoint")
return
}

cmd := &RecommendationApplyCommand{Meta: Meta{Ui: ui, flagAddress: url}}
prefix := rec.ID[:5]
args := complete.Args{Last: prefix}
predictor := cmd.AutocompleteArgs()

res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(rec.ID, res[0])
}
17 changes: 17 additions & 0 deletions command/recommendation_dismiss.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

"github.com/mitchellh/cli"
"github.com/posener/complete"

"github.com/hashicorp/nomad/api/contexts"
)

// Ensure RecommendationDismissCommand satisfies the cli.Command interface.
Expand Down Expand Up @@ -39,6 +41,21 @@ func (r *RecommendationDismissCommand) AutocompleteFlags() complete.Flags {
complete.Flags{})
}

func (r *RecommendationDismissCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictFunc(func(a complete.Args) []string {
client, err := r.Meta.Client()
if err != nil {
return nil
}

resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Recommendations, nil)
if err != nil {
return []string{}
}
return resp.Matches[contexts.Recommendations]
})
}

// Name returns the name of this command.
func (r *RecommendationDismissCommand) Name() string { return "recommendation dismiss" }

Expand Down
45 changes: 45 additions & 0 deletions command/recommendation_dismiss_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -80,3 +82,46 @@ func TestRecommendationDismissCommand_Run(t *testing.T) {
require.Error(err, "not found")
require.Nil(recInfo)
}

func TestRecommendationDismissCommand_AutocompleteArgs(t *testing.T) {
assert := assert.New(t)
t.Parallel()

srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()

// Register a test job to write a recommendation against.
ui := cli.NewMockUi()
testJob := testJob("recommendation_list")
regResp, _, err := client.Jobs().Register(testJob, nil)
require.NoError(t, err)
registerCode := waitForSuccess(ui, client, fullId, t, regResp.EvalID)
require.Equal(t, 0, registerCode)

// Write a recommendation.
rec := &api.Recommendation{
JobID: *testJob.ID,
Group: *testJob.TaskGroups[0].Name,
Task: testJob.TaskGroups[0].Tasks[0].Name,
Resource: "CPU",
Value: 1050,
Meta: map[string]interface{}{"test-meta-entry": "test-meta-value"},
Stats: map[string]float64{"p13": 1.13},
}
rec, _, err = client.Recommendations().Upsert(rec, nil)
if srv.Enterprise {
require.NoError(t, err)
} else {
require.Error(t, err, "Nomad Enterprise only endpoint")
return
}

cmd := &RecommendationDismissCommand{Meta: Meta{Ui: ui, flagAddress: url}}
prefix := rec.ID[:5]
args := complete.Args{Last: prefix}
predictor := cmd.AutocompleteArgs()

res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(rec.ID, res[0])
}
17 changes: 17 additions & 0 deletions command/recommendation_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (

"github.com/mitchellh/cli"
"github.com/posener/complete"

"github.com/hashicorp/nomad/api/contexts"
)

// Ensure RecommendationInfoCommand satisfies the cli.Command interface.
Expand Down Expand Up @@ -52,6 +54,21 @@ func (r *RecommendationInfoCommand) AutocompleteFlags() complete.Flags {
})
}

func (r *RecommendationInfoCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictFunc(func(a complete.Args) []string {
client, err := r.Meta.Client()
if err != nil {
return nil
}

resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Recommendations, nil)
if err != nil {
return []string{}
}
return resp.Matches[contexts.Recommendations]
})
}

// Name returns the name of this command.
func (r *RecommendationInfoCommand) Name() string { return "recommendation info" }

Expand Down
45 changes: 45 additions & 0 deletions command/recommendation_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -81,3 +83,46 @@ func TestRecommendationInfoCommand_Run(t *testing.T) {
require.Contains(out, recResp.ID)
}
}

func TestRecommendationInfoCommand_AutocompleteArgs(t *testing.T) {
assert := assert.New(t)
t.Parallel()

srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()

// Register a test job to write a recommendation against.
ui := cli.NewMockUi()
testJob := testJob("recommendation_list")
regResp, _, err := client.Jobs().Register(testJob, nil)
require.NoError(t, err)
registerCode := waitForSuccess(ui, client, fullId, t, regResp.EvalID)
require.Equal(t, 0, registerCode)

// Write a recommendation.
rec := &api.Recommendation{
JobID: *testJob.ID,
Group: *testJob.TaskGroups[0].Name,
Task: testJob.TaskGroups[0].Tasks[0].Name,
Resource: "CPU",
Value: 1050,
Meta: map[string]interface{}{"test-meta-entry": "test-meta-value"},
Stats: map[string]float64{"p13": 1.13},
}
rec, _, err = client.Recommendations().Upsert(rec, nil)
if srv.Enterprise {
require.NoError(t, err)
} else {
require.Error(t, err, "Nomad Enterprise only endpoint")
return
}

cmd := &RecommendationInfoCommand{Meta: Meta{Ui: ui, flagAddress: url}}
prefix := rec.ID[:5]
args := complete.Args{Last: prefix}
predictor := cmd.AutocompleteArgs()

res := predictor.Predict(args)
assert.Equal(1, len(res))
assert.Equal(rec.ID, res[0])
}
22 changes: 3 additions & 19 deletions command/recommendation_list_test.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,21 @@
package command

import (
"fmt"
"sort"
"testing"

"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/hashicorp/nomad/api"
)

func TestRecommendationListCommand_Run(t *testing.T) {
require := require.New(t)
t.Parallel()
srv, client, url := testServer(t, true, nil)
defer srv.Shutdown()
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")
}
if _, ok := nodes[0].Drivers["mock_driver"]; !ok {
return false, fmt.Errorf("mock_driver not ready")
}
return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
})

ui := cli.NewMockUi()
cmd := &RecommendationListCommand{Meta: Meta{Ui: ui}}
Expand Down Expand Up @@ -89,7 +73,7 @@ func TestRecommendationListCommand_Run(t *testing.T) {
}
}

func TestRecommendationList_Sort(t *testing.T) {
func TestRecommendationListCommand_Sort(t *testing.T) {
testCases := []struct {
inputRecommendationList []*api.Recommendation
expectedOutputList []*api.Recommendation
Expand Down
21 changes: 11 additions & 10 deletions nomad/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,17 @@ var (
type Context string

const (
Allocs Context = "allocs"
Deployments Context = "deployment"
Evals Context = "evals"
Jobs Context = "jobs"
Nodes Context = "nodes"
Namespaces Context = "namespaces"
Quotas Context = "quotas"
All Context = "all"
Plugins Context = "plugins"
Volumes Context = "volumes"
Allocs Context = "allocs"
Deployments Context = "deployment"
Evals Context = "evals"
Jobs Context = "jobs"
Nodes Context = "nodes"
Namespaces Context = "namespaces"
Quotas Context = "quotas"
Recommendations Context = "recommendations"
All Context = "all"
Plugins Context = "plugins"
Volumes Context = "volumes"
)

// NamespacedID is a tuple of an ID and a namespace
Expand Down
21 changes: 11 additions & 10 deletions vendor/github.com/hashicorp/nomad/api/contexts/contexts.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e77fbe9

Please sign in to comment.