open-nomad/command/recommendation_list_test.go
James Rasell fe92d8b3cb
cli: add recommendation commands.
Adds new CLI commands for applying, dismissing and detailing Nomad
recommendations under a new top level recommendation command.
2020-11-06 11:16:24 +01:00

176 lines
7.3 KiB
Go

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"
)
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}}
// Perform an initial list, which should return zero results.
code := cmd.Run([]string{"-address=" + url})
if srv.Enterprise {
require.Equal(0, code)
out := ui.OutputWriter.String()
require.Contains(out, "No recommendations found")
} else {
require.Equal(1, code)
require.Contains(ui.ErrorWriter.String(), "Nomad Enterprise only endpoint")
}
// Register a test job to write a recommendation against.
testJob := testJob("recommendation_list")
regResp, _, err := client.Jobs().Register(testJob, nil)
require.NoError(err)
registerCode := waitForSuccess(ui, client, fullId, t, regResp.EvalID)
require.Equal(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},
}
_, _, err = client.Recommendations().Upsert(&rec, nil)
if srv.Enterprise {
require.NoError(err)
} else {
require.Error(err, "Nomad Enterprise only endpoint")
}
// Perform a new list which should yield results.
code = cmd.Run([]string{"-address=" + url})
if srv.Enterprise {
require.Equal(0, code)
out := ui.OutputWriter.String()
require.Contains(out, "ID")
require.Contains(out, "Job")
require.Contains(out, "Group")
require.Contains(out, "Task")
require.Contains(out, "Resource")
require.Contains(out, "Value")
require.Contains(out, "CPU")
} else {
require.Equal(1, code)
require.Contains(ui.ErrorWriter.String(), "Nomad Enterprise only endpoint")
}
}
func TestRecommendationList_Sort(t *testing.T) {
testCases := []struct {
inputRecommendationList []*api.Recommendation
expectedOutputList []*api.Recommendation
name string
}{
{
inputRecommendationList: []*api.Recommendation{
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
},
expectedOutputList: []*api.Recommendation{
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
},
name: "single job with both resources",
},
{
inputRecommendationList: []*api.Recommendation{
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "CPU"},
},
expectedOutputList: []*api.Recommendation{
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
},
name: "single job with multiple groups",
},
{
inputRecommendationList: []*api.Recommendation{
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "CPU"},
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "CPU"},
},
expectedOutputList: []*api.Recommendation{
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
},
name: "multiple jobs",
},
{
inputRecommendationList: []*api.Recommendation{
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "cefault", JobID: "distro", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "CPU"},
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "cefault", JobID: "distro", Group: "cache", Task: "redis", Resource: "CPU"},
},
expectedOutputList: []*api.Recommendation{
{Namespace: "cefault", JobID: "distro", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "cefault", JobID: "distro", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "distro", Group: "cache", Task: "redis", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "mongodb", Resource: "MemoryMB"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "CPU"},
{Namespace: "default", JobID: "example", Group: "cache", Task: "redis", Resource: "MemoryMB"},
},
name: "multiple namespaces",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
sortedRecs := recommendationList{r: tc.inputRecommendationList}
sort.Sort(sortedRecs)
assert.Equal(t, tc.expectedOutputList, sortedRecs.r, tc.name)
})
}
}