Merge pull request #9025 from hashicorp/f-gh-8649
cli: add policy list and info to new scaling cmd.
This commit is contained in:
commit
b7dac9020f
|
@ -618,6 +618,26 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
"scaling": func() (cli.Command, error) {
|
||||||
|
return &ScalingCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
"scaling policy": func() (cli.Command, error) {
|
||||||
|
return &ScalingPolicyCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
"scaling policy info": func() (cli.Command, error) {
|
||||||
|
return &ScalingPolicyInfoCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
"scaling policy list": func() (cli.Command, error) {
|
||||||
|
return &ScalingPolicyListCommand{
|
||||||
|
Meta: meta,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
"sentinel": func() (cli.Command, error) {
|
"sentinel": func() (cli.Command, error) {
|
||||||
return &SentinelCommand{
|
return &SentinelCommand{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
|
|
38
command/scaling.go
Normal file
38
command/scaling.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure ScalingCommand satisfies the cli.Command interface.
|
||||||
|
var _ cli.Command = &ScalingCommand{}
|
||||||
|
|
||||||
|
// ScalingCommand implements cli.Command.
|
||||||
|
type ScalingCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help satisfies the cli.Command Help function.
|
||||||
|
func (s *ScalingCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: nomad scaling <subcommand> [options]
|
||||||
|
|
||||||
|
This command groups subcommands for interacting with the scaling API.
|
||||||
|
|
||||||
|
Please see the individual subcommand help for detailed usage information.
|
||||||
|
`
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synopsis satisfies the cli.Command Synopsis function.
|
||||||
|
func (s *ScalingCommand) Synopsis() string {
|
||||||
|
return "Interact with the Nomad scaling endpoint"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of this command.
|
||||||
|
func (s *ScalingCommand) Name() string { return "scaling" }
|
||||||
|
|
||||||
|
// Run satisfies the cli.Command Run function.
|
||||||
|
func (s *ScalingCommand) Run(_ []string) int { return cli.RunResultHelp }
|
79
command/scaling_policy.go
Normal file
79
command/scaling_policy.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure ScalingPolicyCommand satisfies the cli.Command interface.
|
||||||
|
var _ cli.Command = &ScalingPolicyCommand{}
|
||||||
|
|
||||||
|
// ScalingPolicyCommand implements cli.Command.
|
||||||
|
type ScalingPolicyCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help satisfies the cli.Command Help function.
|
||||||
|
func (s *ScalingPolicyCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: nomad scaling policy <subcommand> [options]
|
||||||
|
|
||||||
|
This command groups subcommands for interacting with scaling policies. Scaling
|
||||||
|
policies can be used by an external autoscaler to perform scaling actions on
|
||||||
|
Nomad targets.
|
||||||
|
|
||||||
|
List policies:
|
||||||
|
|
||||||
|
$ nomad scaling policy list
|
||||||
|
|
||||||
|
Detail an individual scaling policy:
|
||||||
|
|
||||||
|
$ nomad scaling policy info <policy_id>
|
||||||
|
|
||||||
|
Please see the individual subcommand help for detailed usage information.
|
||||||
|
`
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synopsis satisfies the cli.Command Synopsis function.
|
||||||
|
func (s *ScalingPolicyCommand) Synopsis() string {
|
||||||
|
return "Interact with Nomad scaling policies"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of this command.
|
||||||
|
func (s *ScalingPolicyCommand) Name() string { return "scaling policy" }
|
||||||
|
|
||||||
|
// Run satisfies the cli.Command Run function.
|
||||||
|
func (s *ScalingPolicyCommand) Run(_ []string) int { return cli.RunResultHelp }
|
||||||
|
|
||||||
|
// formatScalingPolicyTarget is a command helper that correctly formats a
|
||||||
|
// scaling policy target map into a command string output.
|
||||||
|
func formatScalingPolicyTarget(t map[string]string) string {
|
||||||
|
var ns, j, g string
|
||||||
|
var other []string
|
||||||
|
|
||||||
|
for k, v := range t {
|
||||||
|
|
||||||
|
s := fmt.Sprintf("%s:%s", k, v)
|
||||||
|
|
||||||
|
switch strings.ToLower(k) {
|
||||||
|
case "namespace":
|
||||||
|
ns = s
|
||||||
|
case "job":
|
||||||
|
j = s
|
||||||
|
case "group":
|
||||||
|
g = s
|
||||||
|
default:
|
||||||
|
other = append(other, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out := []string{ns, j, g}
|
||||||
|
|
||||||
|
if len(other) > 0 {
|
||||||
|
out = append(out, other...)
|
||||||
|
}
|
||||||
|
return strings.Trim(strings.Join(out, ","), ",")
|
||||||
|
}
|
126
command/scaling_policy_info.go
Normal file
126
command/scaling_policy_info.go
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure ScalingPolicyInfoCommand satisfies the cli.Command interface.
|
||||||
|
var _ cli.Command = &ScalingPolicyInfoCommand{}
|
||||||
|
|
||||||
|
// ScalingPolicyListCommand implements cli.Command.
|
||||||
|
type ScalingPolicyInfoCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help satisfies the cli.Command Help function.
|
||||||
|
func (s *ScalingPolicyInfoCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: nomad scaling policy info [options] <policy_id>
|
||||||
|
|
||||||
|
Info is used to read the specified scaling policy.
|
||||||
|
|
||||||
|
General Options:
|
||||||
|
|
||||||
|
` + generalOptionsUsage() + `
|
||||||
|
|
||||||
|
Policy Info Options:
|
||||||
|
|
||||||
|
-json
|
||||||
|
Output the scaling policy in its JSON format.
|
||||||
|
|
||||||
|
-t
|
||||||
|
Format and display the scaling policy using a Go template.
|
||||||
|
`
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synopsis satisfies the cli.Command Synopsis function.
|
||||||
|
func (s *ScalingPolicyInfoCommand) Synopsis() string {
|
||||||
|
return "Display an individual Nomad scaling policy"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ScalingPolicyInfoCommand) AutocompleteFlags() complete.Flags {
|
||||||
|
return mergeAutocompleteFlags(s.Meta.AutocompleteFlags(FlagSetClient),
|
||||||
|
complete.Flags{
|
||||||
|
"-json": complete.PredictNothing,
|
||||||
|
"-t": complete.PredictAnything,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of this command.
|
||||||
|
func (s *ScalingPolicyInfoCommand) Name() string { return "scaling policy info" }
|
||||||
|
|
||||||
|
// Run satisfies the cli.Command Run function.
|
||||||
|
func (s *ScalingPolicyInfoCommand) Run(args []string) int {
|
||||||
|
var json bool
|
||||||
|
var tmpl string
|
||||||
|
|
||||||
|
flags := s.Meta.FlagSet(s.Name(), FlagSetClient)
|
||||||
|
flags.Usage = func() { s.Ui.Output(s.Help()) }
|
||||||
|
flags.BoolVar(&json, "json", false, "")
|
||||||
|
flags.StringVar(&tmpl, "t", "", "")
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if args = flags.Args(); len(args) != 1 {
|
||||||
|
s.Ui.Error("This command takes one argument: <policy_id>")
|
||||||
|
s.Ui.Error(commandErrorText(s))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the policy ID.
|
||||||
|
policyID := args[0]
|
||||||
|
|
||||||
|
// Get the HTTP client.
|
||||||
|
client, err := s.Meta.Client()
|
||||||
|
if err != nil {
|
||||||
|
s.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, _, err := client.Scaling().GetPolicy(policyID, nil)
|
||||||
|
if err != nil {
|
||||||
|
s.Ui.Error(fmt.Sprintf("Error listing scaling policies: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user has specified to output the policy as JSON or using a
|
||||||
|
// template then perform this action for the entire object and exit the
|
||||||
|
// command.
|
||||||
|
if json || len(tmpl) > 0 {
|
||||||
|
out, err := Format(json, tmpl, policy)
|
||||||
|
if err != nil {
|
||||||
|
s.Ui.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
s.Ui.Output(out)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the policy document which is a freeform map[string]interface{}
|
||||||
|
// and therefore can only be made pretty to a certain extent. Do this
|
||||||
|
// before the rest of the formatting so any errors are clearly passed back
|
||||||
|
// to the CLI.
|
||||||
|
out, err := Format(true, "", policy.Policy)
|
||||||
|
if err != nil {
|
||||||
|
s.Ui.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
info := []string{
|
||||||
|
fmt.Sprintf("ID|%s", policy.ID),
|
||||||
|
fmt.Sprintf("Enabled|%v", *policy.Enabled),
|
||||||
|
fmt.Sprintf("Target|%s", formatScalingPolicyTarget(policy.Target)),
|
||||||
|
fmt.Sprintf("Min|%v", *policy.Min),
|
||||||
|
fmt.Sprintf("Max|%v", *policy.Max),
|
||||||
|
}
|
||||||
|
s.Ui.Output(formatKV(info))
|
||||||
|
s.Ui.Output("\nPolicy:")
|
||||||
|
s.Ui.Output(out)
|
||||||
|
return 0
|
||||||
|
}
|
88
command/scaling_policy_info_test.go
Normal file
88
command/scaling_policy_info_test.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/api"
|
||||||
|
"github.com/hashicorp/nomad/helper"
|
||||||
|
"github.com/hashicorp/nomad/testutil"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScalingPolicyInfoCommand_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 := &ScalingPolicyInfoCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// Calling without the policyID should result in an error.
|
||||||
|
if code := cmd.Run([]string{"-address=" + url}); code != 1 {
|
||||||
|
t.Fatalf("expected cmd run exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, "This command takes one argument: <policy_id>") {
|
||||||
|
t.Fatalf("expected argument error within output: %v", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform an initial info, which should return zero results.
|
||||||
|
if code := cmd.Run([]string{"-address=" + url, "scaling_policy_info"}); code != 1 {
|
||||||
|
t.Fatalf("expected cmd run exit code 1, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.ErrorWriter.String(); !strings.Contains(out, "404 (policy not found)") {
|
||||||
|
t.Fatalf("expected 404 not found within output: %v", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a test job.
|
||||||
|
job := testJob("scaling_policy_info")
|
||||||
|
|
||||||
|
// Generate an example scaling policy.
|
||||||
|
job.TaskGroups[0].Scaling = &api.ScalingPolicy{
|
||||||
|
Enabled: helper.BoolToPtr(true),
|
||||||
|
Min: helper.Int64ToPtr(1),
|
||||||
|
Max: helper.Int64ToPtr(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the job.
|
||||||
|
resp, _, err := client.Jobs().Register(job, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
|
||||||
|
t.Fatalf("expected waitForSuccess exit code 0, got: %d", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the generated policyID.
|
||||||
|
policies, _, err := client.Scaling().ListPolicies(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
numPolicies := len(policies)
|
||||||
|
if numPolicies == 0 || numPolicies > 1 {
|
||||||
|
t.Fatalf("expected 1 policy return, got %v", numPolicies)
|
||||||
|
}
|
||||||
|
|
||||||
|
if code := cmd.Run([]string{"-address=" + url, policies[0].ID}); code != 0 {
|
||||||
|
t.Fatalf("expected cmd run exit code 0, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.OutputWriter.String(); !strings.Contains(out, "Policy:") {
|
||||||
|
t.Fatalf("expected policy ID within output: %v", out)
|
||||||
|
}
|
||||||
|
}
|
151
command/scaling_policy_list.go
Normal file
151
command/scaling_policy_list.go
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/api"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/posener/complete"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure ScalingPolicyListCommand satisfies the cli.Command interface.
|
||||||
|
var _ cli.Command = &ScalingPolicyListCommand{}
|
||||||
|
|
||||||
|
// ScalingPolicyListCommand implements cli.Command.
|
||||||
|
type ScalingPolicyListCommand struct {
|
||||||
|
Meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Help satisfies the cli.Command Help function.
|
||||||
|
func (s *ScalingPolicyListCommand) Help() string {
|
||||||
|
helpText := `
|
||||||
|
Usage: nomad scaling policy list [options]
|
||||||
|
|
||||||
|
List is used to list the currently configured scaling policies.
|
||||||
|
|
||||||
|
General Options:
|
||||||
|
|
||||||
|
` + generalOptionsUsage() + `
|
||||||
|
|
||||||
|
Policy Info Options:
|
||||||
|
|
||||||
|
-json
|
||||||
|
Output the scaling policy in its JSON format.
|
||||||
|
|
||||||
|
-t
|
||||||
|
Format and display the scaling policy using a Go template.
|
||||||
|
`
|
||||||
|
return strings.TrimSpace(helpText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synopsis satisfies the cli.Command Synopsis function.
|
||||||
|
func (s *ScalingPolicyListCommand) Synopsis() string {
|
||||||
|
return "Display all Nomad scaling policies"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ScalingPolicyListCommand) AutocompleteFlags() complete.Flags {
|
||||||
|
return mergeAutocompleteFlags(s.Meta.AutocompleteFlags(FlagSetClient),
|
||||||
|
complete.Flags{
|
||||||
|
"-json": complete.PredictNothing,
|
||||||
|
"-t": complete.PredictAnything,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of this command.
|
||||||
|
func (s *ScalingPolicyListCommand) Name() string { return "scaling policy list" }
|
||||||
|
|
||||||
|
// Run satisfies the cli.Command Run function.
|
||||||
|
func (s *ScalingPolicyListCommand) Run(args []string) int {
|
||||||
|
var json bool
|
||||||
|
var tmpl string
|
||||||
|
|
||||||
|
flags := s.Meta.FlagSet(s.Name(), FlagSetClient)
|
||||||
|
flags.Usage = func() { s.Ui.Output(s.Help()) }
|
||||||
|
flags.BoolVar(&json, "json", false, "")
|
||||||
|
flags.StringVar(&tmpl, "t", "", "")
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if args = flags.Args(); len(args) > 0 {
|
||||||
|
s.Ui.Error("This command takes no arguments")
|
||||||
|
s.Ui.Error(commandErrorText(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the HTTP client.
|
||||||
|
client, err := s.Meta.Client()
|
||||||
|
if err != nil {
|
||||||
|
s.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
policies, _, err := client.Scaling().ListPolicies(nil)
|
||||||
|
if err != nil {
|
||||||
|
s.Ui.Error(fmt.Sprintf("Error listing scaling policies: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(policies) == 0 {
|
||||||
|
s.Ui.Output("No policies found")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if json || len(tmpl) > 0 {
|
||||||
|
out, err := Format(json, tmpl, policies)
|
||||||
|
if err != nil {
|
||||||
|
s.Ui.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
s.Ui.Output(out)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the output table header.
|
||||||
|
output := []string{"ID|Enabled|Target"}
|
||||||
|
|
||||||
|
// Sort the list of policies based on their target.
|
||||||
|
sortedPolicies := scalingPolicyStubList{policies: policies}
|
||||||
|
sort.Sort(sortedPolicies)
|
||||||
|
|
||||||
|
// Iterate the policies and add to the output.
|
||||||
|
for _, policy := range sortedPolicies.policies {
|
||||||
|
output = append(output, fmt.Sprintf(
|
||||||
|
"%s|%v|%s",
|
||||||
|
policy.ID, policy.Enabled, formatScalingPolicyTarget(policy.Target)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output.
|
||||||
|
s.Ui.Output(formatList(output))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// scalingPolicyStubList is a wrapper around []*api.ScalingPolicyListStub that
|
||||||
|
// list us sort the policies alphabetically based on their target.
|
||||||
|
type scalingPolicyStubList struct {
|
||||||
|
policies []*api.ScalingPolicyListStub
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len satisfies the Len function of the sort.Interface interface.
|
||||||
|
func (s scalingPolicyStubList) Len() int { return len(s.policies) }
|
||||||
|
|
||||||
|
// Swap satisfies the Swap function of the sort.Interface interface.
|
||||||
|
func (s scalingPolicyStubList) Swap(i, j int) {
|
||||||
|
s.policies[i], s.policies[j] = s.policies[j], s.policies[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less satisfies the Less function of the sort.Interface interface.
|
||||||
|
func (s scalingPolicyStubList) Less(i, j int) bool {
|
||||||
|
|
||||||
|
iTarget := formatScalingPolicyTarget(s.policies[i].Target)
|
||||||
|
jTarget := formatScalingPolicyTarget(s.policies[j].Target)
|
||||||
|
|
||||||
|
stringList := []string{iTarget, jTarget}
|
||||||
|
sort.Strings(stringList)
|
||||||
|
|
||||||
|
if stringList[0] == iTarget {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
83
command/scaling_policy_list_test.go
Normal file
83
command/scaling_policy_list_test.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/api"
|
||||||
|
"github.com/hashicorp/nomad/helper"
|
||||||
|
"github.com/hashicorp/nomad/testutil"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScalingPolicyListCommand_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 := &ScalingPolicyListCommand{Meta: Meta{Ui: ui}}
|
||||||
|
|
||||||
|
// Perform an initial list, which should return zero results.
|
||||||
|
if code := cmd.Run([]string{"-address=" + url}); code != 0 {
|
||||||
|
t.Fatalf("expected cmd run exit code 0, got: %d", code)
|
||||||
|
}
|
||||||
|
if out := ui.OutputWriter.String(); !strings.Contains(out, "No policies found") {
|
||||||
|
t.Fatalf("expected no policies found within output: %v", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate two test jobs.
|
||||||
|
jobs := []*api.Job{testJob("scaling_policy_list_1"), testJob("scaling_policy_list_2")}
|
||||||
|
|
||||||
|
// Generate an example scaling policy.
|
||||||
|
scalingPolicy := api.ScalingPolicy{
|
||||||
|
Enabled: helper.BoolToPtr(true),
|
||||||
|
Min: helper.Int64ToPtr(1),
|
||||||
|
Max: helper.Int64ToPtr(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate the jobs, add the scaling policy and register.
|
||||||
|
for _, job := range jobs {
|
||||||
|
job.TaskGroups[0].Scaling = &scalingPolicy
|
||||||
|
resp, _, err := client.Jobs().Register(job, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if code := waitForSuccess(ui, client, fullId, t, resp.EvalID); code != 0 {
|
||||||
|
t.Fatalf("expected waitForSuccess exit code 0, got: %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a new list which should yield results..
|
||||||
|
if code := cmd.Run([]string{"-address=" + url}); code != 0 {
|
||||||
|
t.Fatalf("expected cmd run exit code 0, got: %d", code)
|
||||||
|
}
|
||||||
|
out := ui.OutputWriter.String()
|
||||||
|
if !strings.Contains(out, "ID") ||
|
||||||
|
!strings.Contains(out, "Enabled") ||
|
||||||
|
!strings.Contains(out, "Target") {
|
||||||
|
t.Fatalf("expected table headers within output: %v", out)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "scaling_policy_list_1") {
|
||||||
|
t.Fatalf("expected job scaling_policy_list_1 within output: %v", out)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "scaling_policy_list_2") {
|
||||||
|
t.Fatalf("expected job scaling_policy_list_2 within output: %v", out)
|
||||||
|
}
|
||||||
|
}
|
49
command/scaling_policy_test.go
Normal file
49
command/scaling_policy_test.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_formatScalingPolicyTarget(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
inputMap map[string]string
|
||||||
|
expectedOutput string
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
inputMap: map[string]string{
|
||||||
|
"Namespace": "default",
|
||||||
|
"Job": "example",
|
||||||
|
"Group": "cache",
|
||||||
|
},
|
||||||
|
expectedOutput: "Namespace:default,Job:example,Group:cache",
|
||||||
|
name: "generic horizontal scaling policy target",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputMap: map[string]string{
|
||||||
|
"Namespace": "default",
|
||||||
|
"Job": "example",
|
||||||
|
"Group": "cache",
|
||||||
|
"Unknown": "alien",
|
||||||
|
},
|
||||||
|
expectedOutput: "Namespace:default,Job:example,Group:cache,Unknown:alien",
|
||||||
|
name: "extra key in input mapping",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputMap: map[string]string{
|
||||||
|
"Namespace": "default",
|
||||||
|
},
|
||||||
|
expectedOutput: "Namespace:default",
|
||||||
|
name: "single entry in map",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
actualOutput := formatScalingPolicyTarget(tc.inputMap)
|
||||||
|
assert.Equal(t, tc.expectedOutput, actualOutput, tc.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue