diff --git a/command/scaling_policy_info.go b/command/scaling_policy_info.go index cf321fe8b..24123f8f0 100644 --- a/command/scaling_policy_info.go +++ b/command/scaling_policy_info.go @@ -6,6 +6,9 @@ import ( "github.com/mitchellh/cli" "github.com/posener/complete" + + "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/api/contexts" ) // Ensure ScalingPolicyInfoCommand satisfies the cli.Command interface. @@ -54,6 +57,21 @@ func (s *ScalingPolicyInfoCommand) AutocompleteFlags() complete.Flags { }) } +func (c *ScalingPolicyInfoCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictFunc(func(a complete.Args) []string { + client, err := c.Meta.Client() + if err != nil { + return nil + } + + resp, _, err := client.Search().PrefixSearch(a.Last, contexts.ScalingPolicies, nil) + if err != nil { + return []string{} + } + return resp.Matches[contexts.ScalingPolicies] + }) +} + // Name returns the name of this command. func (s *ScalingPolicyInfoCommand) Name() string { return "scaling policy info" } @@ -70,15 +88,6 @@ func (s *ScalingPolicyInfoCommand) Run(args []string) int { return 1 } - if args = flags.Args(); len(args) != 1 { - s.Ui.Error("This command takes one argument: ") - 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 { @@ -86,17 +95,16 @@ func (s *ScalingPolicyInfoCommand) Run(args []string) int { 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 - } + args = flags.Args() - // 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) + // Formatted list mode if no policy ID + if len(args) == 0 && (json || len(tmpl) > 0) { + policies, _, err := client.Scaling().ListPolicies(nil) + if err != nil { + s.Ui.Error(fmt.Sprintf("Error listing scaling policies: %v", err)) + return 1 + } + out, err := Format(json, tmpl, policies) if err != nil { s.Ui.Error(err.Error()) return 1 @@ -105,14 +113,65 @@ func (s *ScalingPolicyInfoCommand) Run(args []string) int { return 0 } + if len(args) != 1 { + s.Ui.Error("This command takes one of the following argument conditions:") + s.Ui.Error(" * A single ") + s.Ui.Error(" * No arguments, with output format specified") + s.Ui.Error(commandErrorText(s)) + return 1 + } + policyID := args[0] + if len(policyID) == 1 { + s.Ui.Error("Identifier must contain at least two characters.") + return 1 + } + + policyID = sanitizeUUIDPrefix(policyID) + policies, _, err := client.Scaling().ListPolicies(&api.QueryOptions{ + Prefix: policyID, + }) + if err != nil { + s.Ui.Error(fmt.Sprintf("Error querying scaling policy: %v", err)) + return 1 + } + if len(policies) == 0 { + s.Ui.Error(fmt.Sprintf("No scaling policies with prefix or id %q found", policyID)) + return 1 + } + if len(policies) > 1 { + out := formatScalingPolicies(policies) + s.Ui.Output(fmt.Sprintf("Prefix matched multiple scaling policies\n\n%s", out)) + return 0 + } + + policy, _, err := client.Scaling().GetPolicy(policies[0].ID, nil) + if err != nil { + s.Ui.Error(fmt.Sprintf("Error querying scaling policy: %s", err)) + return 1 + } + + 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 + out := "" + if len(policy.Policy) > 0 { + out, err = Format(true, "", policy.Policy) + if err != nil { + s.Ui.Error(err.Error()) + return 1 + } } info := []string{ diff --git a/command/scaling_policy_info_test.go b/command/scaling_policy_info_test.go index 0eacbc025..44e8273d9 100644 --- a/command/scaling_policy_info_test.go +++ b/command/scaling_policy_info_test.go @@ -35,10 +35,10 @@ func TestScalingPolicyInfoCommand_Run(t *testing.T) { cmd := &ScalingPolicyInfoCommand{Meta: Meta{Ui: ui}} // Calling without the policyID should result in an error. - if code := cmd.Run([]string{"-address=" + url}); code != 1 { + if code := cmd.Run([]string{"-address=" + url, "first", "second"}); 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: ") { + if out := ui.ErrorWriter.String(); !strings.Contains(out, "This command takes one of the following argument conditions") { t.Fatalf("expected argument error within output: %v", out) } @@ -46,8 +46,8 @@ func TestScalingPolicyInfoCommand_Run(t *testing.T) { 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) + if out := ui.ErrorWriter.String(); !strings.Contains(out, `No scaling policies with prefix or id "scaling_policy_inf" found`) { + t.Fatalf("expected 'no policies found' within output: %v", out) } // Generate a test job. diff --git a/command/scaling_policy_list.go b/command/scaling_policy_list.go index 783655b88..e2ecfdb6f 100644 --- a/command/scaling_policy_list.go +++ b/command/scaling_policy_list.go @@ -87,6 +87,7 @@ func (s *ScalingPolicyListCommand) Run(args []string) int { if args = flags.Args(); len(args) > 0 { s.Ui.Error("This command takes no arguments") s.Ui.Error(commandErrorText(s)) + return 1 } // Get the HTTP client. @@ -111,11 +112,6 @@ func (s *ScalingPolicyListCommand) Run(args []string) int { 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 { @@ -126,23 +122,30 @@ func (s *ScalingPolicyListCommand) Run(args []string) int { return 0 } + output := formatScalingPolicies(policies) + s.Ui.Output(output) + return 0 +} + +func formatScalingPolicies(stubs []*api.ScalingPolicyListStub) string { + if len(stubs) == 0 { + return "No policies found" + } + // Create the output table header. - output := []string{"ID|Enabled|Type|Target"} + policies := []string{"ID|Enabled|Type|Target"} // Sort the list of policies based on their target. - sortedPolicies := scalingPolicyStubList{policies: policies} + sortedPolicies := scalingPolicyStubList{policies: stubs} sort.Sort(sortedPolicies) // Iterate the policies and add to the output. for _, policy := range sortedPolicies.policies { - output = append(output, fmt.Sprintf( + policies = append(policies, fmt.Sprintf( "%s|%v|%s|%s", policy.ID, policy.Enabled, policy.Type, formatScalingPolicyTarget(policy.Target))) } - - // Output. - s.Ui.Output(formatList(output)) - return 0 + return formatList(policies) } // scalingPolicyStubList is a wrapper around []*api.ScalingPolicyListStub that