github.com/bigcommerce/nomad@v0.9.3-bc/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"sort"
     9  	"strings"
    10  	"text/tabwriter"
    11  
    12  	"github.com/hashicorp/nomad/command"
    13  	"github.com/hashicorp/nomad/drivers/docker/docklog"
    14  	"github.com/hashicorp/nomad/version"
    15  	"github.com/mattn/go-colorable"
    16  	"github.com/mitchellh/cli"
    17  	"github.com/sean-/seed"
    18  	"golang.org/x/crypto/ssh/terminal"
    19  )
    20  
    21  var (
    22  	// Hidden hides the commands from both help and autocomplete. Commands that
    23  	// users should not be running should be placed here, versus hiding
    24  	// subcommands from the main help, which should be filtered out of the
    25  	// commands above.
    26  	hidden = []string{
    27  		"alloc-status",
    28  		"check",
    29  		"client-config",
    30  		"eval-status",
    31  		"executor",
    32  		"keygen",
    33  		"keyring",
    34  		"logmon",
    35  		"node-drain",
    36  		"node-status",
    37  		"server-force-leave",
    38  		"server-join",
    39  		"server-members",
    40  		"syslog",
    41  		docklog.PluginName,
    42  	}
    43  
    44  	// aliases is the list of aliases we want users to be aware of. We hide
    45  	// these form the help output but autocomplete them.
    46  	aliases = []string{
    47  		"fs",
    48  		"init",
    49  		"inspect",
    50  		"logs",
    51  		"plan",
    52  		"validate",
    53  	}
    54  
    55  	// Common commands are grouped separately to call them out to operators.
    56  	commonCommands = []string{
    57  		"run",
    58  		"stop",
    59  		"status",
    60  		"alloc",
    61  		"job",
    62  		"node",
    63  		"agent",
    64  	}
    65  )
    66  
    67  func init() {
    68  	seed.Init()
    69  }
    70  
    71  func main() {
    72  	os.Exit(Run(os.Args[1:]))
    73  }
    74  
    75  func Run(args []string) int {
    76  	return RunCustom(args)
    77  }
    78  
    79  func RunCustom(args []string) int {
    80  	// Parse flags into env vars for global use
    81  	args = setupEnv(args)
    82  
    83  	// Create the meta object
    84  	metaPtr := new(command.Meta)
    85  
    86  	// Don't use color if disabled
    87  	color := true
    88  	if os.Getenv(command.EnvNomadCLINoColor) != "" {
    89  		color = false
    90  	}
    91  
    92  	isTerminal := terminal.IsTerminal(int(os.Stdout.Fd()))
    93  	metaPtr.Ui = &cli.BasicUi{
    94  		Reader:      os.Stdin,
    95  		Writer:      colorable.NewColorableStdout(),
    96  		ErrorWriter: colorable.NewColorableStderr(),
    97  	}
    98  
    99  	// The Nomad agent never outputs color
   100  	agentUi := &cli.BasicUi{
   101  		Reader:      os.Stdin,
   102  		Writer:      os.Stdout,
   103  		ErrorWriter: os.Stderr,
   104  	}
   105  
   106  	// Only use colored UI if stdout is a tty, and not disabled
   107  	if isTerminal && color {
   108  		metaPtr.Ui = &cli.ColoredUi{
   109  			ErrorColor: cli.UiColorRed,
   110  			WarnColor:  cli.UiColorYellow,
   111  			InfoColor:  cli.UiColorGreen,
   112  			Ui:         metaPtr.Ui,
   113  		}
   114  	}
   115  
   116  	commands := command.Commands(metaPtr, agentUi)
   117  	cli := &cli.CLI{
   118  		Name:                       "nomad",
   119  		Version:                    version.GetVersion().FullVersionNumber(true),
   120  		Args:                       args,
   121  		Commands:                   commands,
   122  		HiddenCommands:             hidden,
   123  		Autocomplete:               true,
   124  		AutocompleteNoDefaultFlags: true,
   125  		HelpFunc: groupedHelpFunc(
   126  			cli.BasicHelpFunc("nomad"),
   127  		),
   128  		HelpWriter: os.Stdout,
   129  	}
   130  
   131  	exitCode, err := cli.Run()
   132  	if err != nil {
   133  		fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error())
   134  		return 1
   135  	}
   136  
   137  	return exitCode
   138  }
   139  
   140  func groupedHelpFunc(f cli.HelpFunc) cli.HelpFunc {
   141  	return func(commands map[string]cli.CommandFactory) string {
   142  		var b bytes.Buffer
   143  		tw := tabwriter.NewWriter(&b, 0, 2, 6, ' ', 0)
   144  
   145  		fmt.Fprintf(tw, "Usage: nomad [-version] [-help] [-autocomplete-(un)install] <command> [args]\n\n")
   146  		fmt.Fprintf(tw, "Common commands:\n")
   147  		for _, v := range commonCommands {
   148  			printCommand(tw, v, commands[v])
   149  		}
   150  
   151  		// Filter out common commands and aliased commands from the other
   152  		// commands output
   153  		otherCommands := make([]string, 0, len(commands))
   154  		for k := range commands {
   155  			found := false
   156  			for _, v := range commonCommands {
   157  				if k == v {
   158  					found = true
   159  					break
   160  				}
   161  			}
   162  
   163  			for _, v := range aliases {
   164  				if k == v {
   165  					found = true
   166  					break
   167  				}
   168  			}
   169  
   170  			if !found {
   171  				otherCommands = append(otherCommands, k)
   172  			}
   173  		}
   174  		sort.Strings(otherCommands)
   175  
   176  		fmt.Fprintf(tw, "\n")
   177  		fmt.Fprintf(tw, "Other commands:\n")
   178  		for _, v := range otherCommands {
   179  			printCommand(tw, v, commands[v])
   180  		}
   181  
   182  		tw.Flush()
   183  
   184  		return strings.TrimSpace(b.String())
   185  	}
   186  }
   187  
   188  func printCommand(w io.Writer, name string, cmdFn cli.CommandFactory) {
   189  	cmd, err := cmdFn()
   190  	if err != nil {
   191  		panic(fmt.Sprintf("failed to load %q command: %s", name, err))
   192  	}
   193  	fmt.Fprintf(w, "    %s\t%s\n", name, cmd.Synopsis())
   194  }
   195  
   196  // setupEnv parses args and may replace them and sets some env vars to known
   197  // values based on format options
   198  func setupEnv(args []string) []string {
   199  	noColor := false
   200  	for _, arg := range args {
   201  		// Check if color is set
   202  		if arg == "-no-color" || arg == "--no-color" {
   203  			noColor = true
   204  		}
   205  	}
   206  
   207  	// Put back into the env for later
   208  	if noColor {
   209  		os.Setenv(command.EnvNomadCLINoColor, "true")
   210  	}
   211  
   212  	return args
   213  }