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