mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
auto-complete for recommendations CLI, plus OSS components of recommendations prefix search
This commit is contained in:
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
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) {
|
||||
@@ -17,21 +16,6 @@ func TestRecommendationListCommand_Run(t *testing.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}}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
21
vendor/github.com/hashicorp/nomad/api/contexts/contexts.go
generated
vendored
21
vendor/github.com/hashicorp/nomad/api/contexts/contexts.go
generated
vendored
@@ -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"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user