open-nomad/command/status.go
2020-12-09 11:05:18 -08:00

190 lines
4.3 KiB
Go

package command
import (
"fmt"
"strings"
"github.com/hashicorp/nomad/api/contexts"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
type StatusCommand struct {
Meta
// Placeholder bool to allow passing of verbose flags to subcommands.
verbose bool
}
func (s *StatusCommand) Help() string {
helpText := `
Usage: nomad status [options] <identifier>
Display the status output for any given resource. The command will
detect the type of resource being queried and display the appropriate
status output.
General Options:
` + generalOptionsUsage(usageOptsDefault) + `
Status Options:
-verbose
Display full information.
`
return strings.TrimSpace(helpText)
}
func (c *StatusCommand) Synopsis() string {
return "Display the status output for a resource"
}
func (c *StatusCommand) AutocompleteFlags() complete.Flags {
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
complete.Flags{
"-verbose": complete.PredictNothing,
})
}
func (c *StatusCommand) 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.All, nil)
if err != nil {
return []string{}
}
final := make([]string, 0)
for _, matches := range resp.Matches {
if len(matches) == 0 {
continue
}
final = append(final, matches...)
}
return final
})
}
func (c *StatusCommand) Run(args []string) int {
flags := c.Meta.FlagSet("status", FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&c.verbose, "verbose", false, "")
if err := flags.Parse(args); err != nil {
c.Ui.Error(fmt.Sprintf("Error parsing arguments: %q", err))
return 1
}
// Store the original arguments so we can pass them to the routed command
argsCopy := args
// Check that we got exactly one evaluation ID
args = flags.Args()
// Get the HTTP client
client, err := c.Meta.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing client: %q", err))
return 1
}
// If no identifier is provided, default to listing jobs
if len(args) == 0 {
cmd := &JobStatusCommand{Meta: c.Meta}
return cmd.Run(argsCopy)
}
id := args[len(args)-1]
// Query for the context associated with the id
res, _, err := client.Search().PrefixSearch(id, contexts.All, nil)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error querying search with id: %q", err))
return 1
}
if res.Matches == nil {
c.Ui.Error(fmt.Sprintf("No matches returned for query: %q", err))
return 1
}
var match contexts.Context
exactMatches := 0
for ctx, vers := range res.Matches {
if len(vers) > 0 && vers[0] == id {
match = ctx
exactMatches++
}
}
if exactMatches > 1 {
c.logMultiMatchError(id, res.Matches)
return 1
} else if exactMatches == 0 {
matchCount := 0
for ctx, vers := range res.Matches {
l := len(vers)
if l == 1 {
match = ctx
matchCount++
}
// Only a single result should return, as this is a match against a full id
if matchCount > 1 || l > 1 {
c.logMultiMatchError(id, res.Matches)
return 1
}
}
}
var cmd cli.Command
switch match {
case contexts.Evals:
cmd = &EvalStatusCommand{Meta: c.Meta}
case contexts.Nodes:
cmd = &NodeStatusCommand{Meta: c.Meta}
case contexts.Allocs:
cmd = &AllocStatusCommand{Meta: c.Meta}
case contexts.Jobs:
cmd = &JobStatusCommand{Meta: c.Meta}
case contexts.Deployments:
cmd = &DeploymentStatusCommand{Meta: c.Meta}
case contexts.Namespaces:
cmd = &NamespaceStatusCommand{Meta: c.Meta}
case contexts.Quotas:
cmd = &QuotaStatusCommand{Meta: c.Meta}
case contexts.Plugins:
cmd = &PluginStatusCommand{Meta: c.Meta}
case contexts.Volumes:
cmd = &VolumeStatusCommand{Meta: c.Meta}
default:
c.Ui.Error(fmt.Sprintf("Unable to resolve ID: %q", id))
return 1
}
return cmd.Run(argsCopy)
}
// logMultiMatchError is used to log an error message when multiple matches are
// found. The error message logged displays the matched IDs per context.
func (c *StatusCommand) logMultiMatchError(id string, matches map[contexts.Context][]string) {
c.Ui.Error(fmt.Sprintf("Multiple matches found for id %q", id))
for ctx, vers := range matches {
if len(vers) == 0 {
continue
}
c.Ui.Error(fmt.Sprintf("\n%s:", strings.Title(string(ctx))))
c.Ui.Error(strings.Join(vers, ", "))
}
}