github.com/manicqin/nomad@v0.9.5/command/status.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/nomad/api/contexts"
     8  	"github.com/mitchellh/cli"
     9  	"github.com/posener/complete"
    10  )
    11  
    12  type StatusCommand struct {
    13  	Meta
    14  
    15  	// Placeholder bool to allow passing of verbose flags to subcommands.
    16  	verbose bool
    17  }
    18  
    19  func (s *StatusCommand) Help() string {
    20  	helpText := `
    21  Usage: nomad status [options] <identifier>
    22  
    23    Display the status output for any given resource. The command will
    24    detect the type of resource being queried and display the appropriate
    25    status output.
    26  
    27  General Options:
    28  
    29    ` + generalOptionsUsage() + `
    30  
    31  Status Options:
    32  
    33    -verbose
    34      Display full information.
    35  `
    36  
    37  	return strings.TrimSpace(helpText)
    38  }
    39  
    40  func (c *StatusCommand) Synopsis() string {
    41  	return "Display the status output for a resource"
    42  }
    43  
    44  func (c *StatusCommand) AutocompleteFlags() complete.Flags {
    45  	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
    46  		complete.Flags{
    47  			"-verbose": complete.PredictNothing,
    48  		})
    49  }
    50  
    51  func (c *StatusCommand) AutocompleteArgs() complete.Predictor {
    52  	return complete.PredictFunc(func(a complete.Args) []string {
    53  		client, err := c.Meta.Client()
    54  		if err != nil {
    55  			return nil
    56  		}
    57  
    58  		resp, _, err := client.Search().PrefixSearch(a.Last, contexts.All, nil)
    59  		if err != nil {
    60  			return []string{}
    61  		}
    62  
    63  		final := make([]string, 0)
    64  
    65  		for _, matches := range resp.Matches {
    66  			if len(matches) == 0 {
    67  				continue
    68  			}
    69  
    70  			final = append(final, matches...)
    71  		}
    72  
    73  		return final
    74  	})
    75  }
    76  
    77  func (c *StatusCommand) Run(args []string) int {
    78  	flags := c.Meta.FlagSet("status", FlagSetClient)
    79  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    80  	flags.BoolVar(&c.verbose, "verbose", false, "")
    81  
    82  	if err := flags.Parse(args); err != nil {
    83  		c.Ui.Error(fmt.Sprintf("Error parsing arguments: %q", err))
    84  		return 1
    85  	}
    86  
    87  	// Store the original arguments so we can pass them to the routed command
    88  	argsCopy := args
    89  
    90  	// Check that we got exactly one evaluation ID
    91  	args = flags.Args()
    92  
    93  	// Get the HTTP client
    94  	client, err := c.Meta.Client()
    95  	if err != nil {
    96  		c.Ui.Error(fmt.Sprintf("Error initializing client: %q", err))
    97  		return 1
    98  	}
    99  
   100  	// If no identifier is provided, default to listing jobs
   101  	if len(args) == 0 {
   102  		cmd := &JobStatusCommand{Meta: c.Meta}
   103  		return cmd.Run(argsCopy)
   104  	}
   105  
   106  	id := args[len(args)-1]
   107  
   108  	// Query for the context associated with the id
   109  	res, _, err := client.Search().PrefixSearch(id, contexts.All, nil)
   110  	if err != nil {
   111  		c.Ui.Error(fmt.Sprintf("Error querying search with id: %q", err))
   112  		return 1
   113  	}
   114  
   115  	if res.Matches == nil {
   116  		c.Ui.Error(fmt.Sprintf("No matches returned for query: %q", err))
   117  		return 1
   118  	}
   119  
   120  	var match contexts.Context
   121  	exactMatches := 0
   122  	for ctx, vers := range res.Matches {
   123  		if len(vers) > 0 && vers[0] == id {
   124  			match = ctx
   125  			exactMatches++
   126  		}
   127  	}
   128  
   129  	if exactMatches > 1 {
   130  		c.logMultiMatchError(id, res.Matches)
   131  		return 1
   132  	} else if exactMatches == 0 {
   133  		matchCount := 0
   134  		for ctx, vers := range res.Matches {
   135  			l := len(vers)
   136  			if l == 1 {
   137  				match = ctx
   138  				matchCount++
   139  			}
   140  
   141  			// Only a single result should return, as this is a match against a full id
   142  			if matchCount > 1 || l > 1 {
   143  				c.logMultiMatchError(id, res.Matches)
   144  				return 1
   145  			}
   146  		}
   147  	}
   148  
   149  	var cmd cli.Command
   150  	switch match {
   151  	case contexts.Evals:
   152  		cmd = &EvalStatusCommand{Meta: c.Meta}
   153  	case contexts.Nodes:
   154  		cmd = &NodeStatusCommand{Meta: c.Meta}
   155  	case contexts.Allocs:
   156  		cmd = &AllocStatusCommand{Meta: c.Meta}
   157  	case contexts.Jobs:
   158  		cmd = &JobStatusCommand{Meta: c.Meta}
   159  	case contexts.Deployments:
   160  		cmd = &DeploymentStatusCommand{Meta: c.Meta}
   161  	case contexts.Namespaces:
   162  		cmd = &NamespaceStatusCommand{Meta: c.Meta}
   163  	case contexts.Quotas:
   164  		cmd = &QuotaStatusCommand{Meta: c.Meta}
   165  	default:
   166  		c.Ui.Error(fmt.Sprintf("Unable to resolve ID: %q", id))
   167  		return 1
   168  	}
   169  
   170  	return cmd.Run(argsCopy)
   171  }
   172  
   173  // logMultiMatchError is used to log an error message when multiple matches are
   174  // found. The error message logged displays the matched IDs per context.
   175  func (c *StatusCommand) logMultiMatchError(id string, matches map[contexts.Context][]string) {
   176  	c.Ui.Error(fmt.Sprintf("Multiple matches found for id %q", id))
   177  	for ctx, vers := range matches {
   178  		if len(vers) == 0 {
   179  			continue
   180  		}
   181  
   182  		c.Ui.Error(fmt.Sprintf("\n%s:", strings.Title(string(ctx))))
   183  		c.Ui.Error(fmt.Sprintf("%s", strings.Join(vers, ", ")))
   184  	}
   185  }