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

     1  package agent
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"os"
     9  	"os/signal"
    10  	"path/filepath"
    11  	"reflect"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  	"syscall"
    16  	"time"
    17  
    18  	"github.com/armon/go-metrics"
    19  	"github.com/armon/go-metrics/circonus"
    20  	"github.com/armon/go-metrics/datadog"
    21  	"github.com/armon/go-metrics/prometheus"
    22  	"github.com/hashicorp/consul/lib"
    23  	"github.com/hashicorp/go-checkpoint"
    24  	"github.com/hashicorp/go-discover"
    25  	"github.com/hashicorp/go-hclog"
    26  	gsyslog "github.com/hashicorp/go-syslog"
    27  	"github.com/hashicorp/logutils"
    28  	"github.com/hashicorp/nomad/helper"
    29  	flaghelper "github.com/hashicorp/nomad/helper/flag-helpers"
    30  	gatedwriter "github.com/hashicorp/nomad/helper/gated-writer"
    31  	"github.com/hashicorp/nomad/helper/logging"
    32  	"github.com/hashicorp/nomad/nomad/structs/config"
    33  	"github.com/hashicorp/nomad/version"
    34  	"github.com/mitchellh/cli"
    35  	"github.com/posener/complete"
    36  )
    37  
    38  // gracefulTimeout controls how long we wait before forcefully terminating
    39  const gracefulTimeout = 5 * time.Second
    40  
    41  // Command is a Command implementation that runs a Nomad agent.
    42  // The command will not end unless a shutdown message is sent on the
    43  // ShutdownCh. If two messages are sent on the ShutdownCh it will forcibly
    44  // exit.
    45  type Command struct {
    46  	Version    *version.VersionInfo
    47  	Ui         cli.Ui
    48  	ShutdownCh <-chan struct{}
    49  
    50  	args           []string
    51  	agent          *Agent
    52  	httpServer     *HTTPServer
    53  	logFilter      *logutils.LevelFilter
    54  	logOutput      io.Writer
    55  	retryJoinErrCh chan struct{}
    56  }
    57  
    58  func (c *Command) readConfig() *Config {
    59  	var dev bool
    60  	var configPath []string
    61  	var servers string
    62  	var meta []string
    63  
    64  	// Make a new, empty config.
    65  	cmdConfig := &Config{
    66  		Client: &ClientConfig{},
    67  		Consul: &config.ConsulConfig{},
    68  		Ports:  &Ports{},
    69  		Server: &ServerConfig{
    70  			ServerJoin: &ServerJoin{},
    71  		},
    72  		Vault: &config.VaultConfig{},
    73  		ACL:   &ACLConfig{},
    74  	}
    75  
    76  	flags := flag.NewFlagSet("agent", flag.ContinueOnError)
    77  	flags.Usage = func() { c.Ui.Error(c.Help()) }
    78  
    79  	// Role options
    80  	flags.BoolVar(&dev, "dev", false, "")
    81  	flags.BoolVar(&cmdConfig.Server.Enabled, "server", false, "")
    82  	flags.BoolVar(&cmdConfig.Client.Enabled, "client", false, "")
    83  
    84  	// Server-only options
    85  	flags.IntVar(&cmdConfig.Server.BootstrapExpect, "bootstrap-expect", 0, "")
    86  	flags.StringVar(&cmdConfig.Server.EncryptKey, "encrypt", "", "gossip encryption key")
    87  	flags.IntVar(&cmdConfig.Server.RaftProtocol, "raft-protocol", 0, "")
    88  	flags.BoolVar(&cmdConfig.Server.RejoinAfterLeave, "rejoin", false, "")
    89  	flags.Var((*flaghelper.StringFlag)(&cmdConfig.Server.ServerJoin.StartJoin), "join", "")
    90  	flags.Var((*flaghelper.StringFlag)(&cmdConfig.Server.ServerJoin.RetryJoin), "retry-join", "")
    91  	flags.IntVar(&cmdConfig.Server.ServerJoin.RetryMaxAttempts, "retry-max", 0, "")
    92  	flags.Var((flaghelper.FuncDurationVar)(func(d time.Duration) error {
    93  		cmdConfig.Server.ServerJoin.RetryInterval = d
    94  		return nil
    95  	}), "retry-interval", "")
    96  
    97  	// Client-only options
    98  	flags.StringVar(&cmdConfig.Client.StateDir, "state-dir", "", "")
    99  	flags.StringVar(&cmdConfig.Client.AllocDir, "alloc-dir", "", "")
   100  	flags.StringVar(&cmdConfig.Client.NodeClass, "node-class", "", "")
   101  	flags.StringVar(&servers, "servers", "", "")
   102  	flags.Var((*flaghelper.StringFlag)(&meta), "meta", "")
   103  	flags.StringVar(&cmdConfig.Client.NetworkInterface, "network-interface", "", "")
   104  	flags.IntVar(&cmdConfig.Client.NetworkSpeed, "network-speed", 0, "")
   105  
   106  	// General options
   107  	flags.Var((*flaghelper.StringFlag)(&configPath), "config", "config")
   108  	flags.StringVar(&cmdConfig.BindAddr, "bind", "", "")
   109  	flags.StringVar(&cmdConfig.Region, "region", "", "")
   110  	flags.StringVar(&cmdConfig.DataDir, "data-dir", "", "")
   111  	flags.StringVar(&cmdConfig.PluginDir, "plugin-dir", "", "")
   112  	flags.StringVar(&cmdConfig.Datacenter, "dc", "", "")
   113  	flags.StringVar(&cmdConfig.LogLevel, "log-level", "", "")
   114  	flags.BoolVar(&cmdConfig.LogJson, "log-json", false, "")
   115  	flags.StringVar(&cmdConfig.NodeName, "node", "", "")
   116  
   117  	// Consul options
   118  	flags.StringVar(&cmdConfig.Consul.Auth, "consul-auth", "", "")
   119  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   120  		cmdConfig.Consul.AutoAdvertise = &b
   121  		return nil
   122  	}), "consul-auto-advertise", "")
   123  	flags.StringVar(&cmdConfig.Consul.CAFile, "consul-ca-file", "", "")
   124  	flags.StringVar(&cmdConfig.Consul.CertFile, "consul-cert-file", "", "")
   125  	flags.StringVar(&cmdConfig.Consul.KeyFile, "consul-key-file", "", "")
   126  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   127  		cmdConfig.Consul.ChecksUseAdvertise = &b
   128  		return nil
   129  	}), "consul-checks-use-advertise", "")
   130  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   131  		cmdConfig.Consul.ClientAutoJoin = &b
   132  		return nil
   133  	}), "consul-client-auto-join", "")
   134  	flags.StringVar(&cmdConfig.Consul.ClientServiceName, "consul-client-service-name", "", "")
   135  	flags.StringVar(&cmdConfig.Consul.ClientHTTPCheckName, "consul-client-http-check-name", "", "")
   136  	flags.StringVar(&cmdConfig.Consul.ServerServiceName, "consul-server-service-name", "", "")
   137  	flags.StringVar(&cmdConfig.Consul.ServerHTTPCheckName, "consul-server-http-check-name", "", "")
   138  	flags.StringVar(&cmdConfig.Consul.ServerSerfCheckName, "consul-server-serf-check-name", "", "")
   139  	flags.StringVar(&cmdConfig.Consul.ServerRPCCheckName, "consul-server-rpc-check-name", "", "")
   140  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   141  		cmdConfig.Consul.ServerAutoJoin = &b
   142  		return nil
   143  	}), "consul-server-auto-join", "")
   144  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   145  		cmdConfig.Consul.EnableSSL = &b
   146  		return nil
   147  	}), "consul-ssl", "")
   148  	flags.StringVar(&cmdConfig.Consul.Token, "consul-token", "", "")
   149  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   150  		cmdConfig.Consul.VerifySSL = &b
   151  		return nil
   152  	}), "consul-verify-ssl", "")
   153  	flags.StringVar(&cmdConfig.Consul.Addr, "consul-address", "", "")
   154  
   155  	// Vault options
   156  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   157  		cmdConfig.Vault.Enabled = &b
   158  		return nil
   159  	}), "vault-enabled", "")
   160  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   161  		cmdConfig.Vault.AllowUnauthenticated = &b
   162  		return nil
   163  	}), "vault-allow-unauthenticated", "")
   164  	flags.StringVar(&cmdConfig.Vault.Token, "vault-token", "", "")
   165  	flags.StringVar(&cmdConfig.Vault.Addr, "vault-address", "", "")
   166  	flags.StringVar(&cmdConfig.Vault.Namespace, "vault-namespace", "", "")
   167  	flags.StringVar(&cmdConfig.Vault.Role, "vault-create-from-role", "", "")
   168  	flags.StringVar(&cmdConfig.Vault.TLSCaFile, "vault-ca-file", "", "")
   169  	flags.StringVar(&cmdConfig.Vault.TLSCaPath, "vault-ca-path", "", "")
   170  	flags.StringVar(&cmdConfig.Vault.TLSCertFile, "vault-cert-file", "", "")
   171  	flags.StringVar(&cmdConfig.Vault.TLSKeyFile, "vault-key-file", "", "")
   172  	flags.Var((flaghelper.FuncBoolVar)(func(b bool) error {
   173  		cmdConfig.Vault.TLSSkipVerify = &b
   174  		return nil
   175  	}), "vault-tls-skip-verify", "")
   176  	flags.StringVar(&cmdConfig.Vault.TLSServerName, "vault-tls-server-name", "", "")
   177  
   178  	// ACL options
   179  	flags.BoolVar(&cmdConfig.ACL.Enabled, "acl-enabled", false, "")
   180  	flags.StringVar(&cmdConfig.ACL.ReplicationToken, "acl-replication-token", "", "")
   181  
   182  	if err := flags.Parse(c.args); err != nil {
   183  		return nil
   184  	}
   185  
   186  	// Split the servers.
   187  	if servers != "" {
   188  		cmdConfig.Client.Servers = strings.Split(servers, ",")
   189  	}
   190  
   191  	// Parse the meta flags.
   192  	metaLength := len(meta)
   193  	if metaLength != 0 {
   194  		cmdConfig.Client.Meta = make(map[string]string, metaLength)
   195  		for _, kv := range meta {
   196  			parts := strings.SplitN(kv, "=", 2)
   197  			if len(parts) != 2 {
   198  				c.Ui.Error(fmt.Sprintf("Error parsing Client.Meta value: %v", kv))
   199  				return nil
   200  			}
   201  			cmdConfig.Client.Meta[parts[0]] = parts[1]
   202  		}
   203  	}
   204  
   205  	// Load the configuration
   206  	var config *Config
   207  	if dev {
   208  		config = DevConfig()
   209  	} else {
   210  		config = DefaultConfig()
   211  	}
   212  
   213  	// Merge in the enterprise overlay
   214  	config.Merge(DefaultEntConfig())
   215  
   216  	for _, path := range configPath {
   217  		current, err := LoadConfig(path)
   218  		if err != nil {
   219  			c.Ui.Error(fmt.Sprintf(
   220  				"Error loading configuration from %s: %s", path, err))
   221  			return nil
   222  		}
   223  
   224  		// The user asked us to load some config here but we didn't find any,
   225  		// so we'll complain but continue.
   226  		if current == nil || reflect.DeepEqual(current, &Config{}) {
   227  			c.Ui.Warn(fmt.Sprintf("No configuration loaded from %s", path))
   228  		}
   229  
   230  		if config == nil {
   231  			config = current
   232  		} else {
   233  			config = config.Merge(current)
   234  		}
   235  	}
   236  
   237  	// Ensure the sub-structs at least exist
   238  	if config.Client == nil {
   239  		config.Client = &ClientConfig{}
   240  	}
   241  	if config.Server == nil {
   242  		config.Server = &ServerConfig{}
   243  	}
   244  
   245  	// Merge any CLI options over config file options
   246  	config = config.Merge(cmdConfig)
   247  
   248  	// Set the version info
   249  	config.Version = c.Version
   250  
   251  	// Normalize binds, ports, addresses, and advertise
   252  	if err := config.normalizeAddrs(); err != nil {
   253  		c.Ui.Error(err.Error())
   254  		return nil
   255  	}
   256  
   257  	// Check to see if we should read the Vault token from the environment
   258  	if config.Vault.Token == "" {
   259  		config.Vault.Token = os.Getenv("VAULT_TOKEN")
   260  	}
   261  
   262  	// Check to see if we should read the Vault namespace from the environment
   263  	if config.Vault.Namespace == "" {
   264  		config.Vault.Namespace = os.Getenv("VAULT_NAMESPACE")
   265  	}
   266  
   267  	// Default the plugin directory to be under that of the data directory if it
   268  	// isn't explicitly specified.
   269  	if config.PluginDir == "" && config.DataDir != "" {
   270  		config.PluginDir = filepath.Join(config.DataDir, "plugins")
   271  	}
   272  
   273  	if !c.isValidConfig(config) {
   274  		return nil
   275  	}
   276  
   277  	return config
   278  }
   279  
   280  func (c *Command) isValidConfig(config *Config) bool {
   281  
   282  	// Check that the server is running in at least one mode.
   283  	if !(config.Server.Enabled || config.Client.Enabled) {
   284  		c.Ui.Error("Must specify either server, client or dev mode for the agent.")
   285  		return false
   286  	}
   287  
   288  	// Set up the TLS configuration properly if we have one.
   289  	// XXX chelseakomlo: set up a TLSConfig New method which would wrap
   290  	// constructor-type actions like this.
   291  	if config.TLSConfig != nil && !config.TLSConfig.IsEmpty() {
   292  		if err := config.TLSConfig.SetChecksum(); err != nil {
   293  			c.Ui.Error(fmt.Sprintf("WARNING: Error when parsing TLS configuration: %v", err))
   294  		}
   295  	}
   296  
   297  	if config.Server.EncryptKey != "" {
   298  		if _, err := config.Server.EncryptBytes(); err != nil {
   299  			c.Ui.Error(fmt.Sprintf("Invalid encryption key: %s", err))
   300  			return false
   301  		}
   302  		keyfile := filepath.Join(config.DataDir, serfKeyring)
   303  		if _, err := os.Stat(keyfile); err == nil {
   304  			c.Ui.Warn("WARNING: keyring exists but -encrypt given, using keyring")
   305  		}
   306  	}
   307  
   308  	// Verify the paths are absolute.
   309  	dirs := map[string]string{
   310  		"data-dir":   config.DataDir,
   311  		"plugin-dir": config.PluginDir,
   312  		"alloc-dir":  config.Client.AllocDir,
   313  		"state-dir":  config.Client.StateDir,
   314  	}
   315  	for k, dir := range dirs {
   316  		if dir == "" {
   317  			continue
   318  		}
   319  
   320  		if !filepath.IsAbs(dir) {
   321  			c.Ui.Error(fmt.Sprintf("%s must be given as an absolute path: got %v", k, dir))
   322  			return false
   323  		}
   324  	}
   325  
   326  	if config.Client.Enabled {
   327  		for k := range config.Client.Meta {
   328  			if !helper.IsValidInterpVariable(k) {
   329  				c.Ui.Error(fmt.Sprintf("Invalid Client.Meta key: %v", k))
   330  				return false
   331  			}
   332  		}
   333  	}
   334  
   335  	if config.DevMode {
   336  		// Skip the rest of the validation for dev mode
   337  		return true
   338  	}
   339  
   340  	// Ensure that we have the directories we need to run.
   341  	if config.Server.Enabled && config.DataDir == "" {
   342  		c.Ui.Error("Must specify data directory")
   343  		return false
   344  	}
   345  
   346  	// The config is valid if the top-level data-dir is set or if both
   347  	// alloc-dir and state-dir are set.
   348  	if config.Client.Enabled && config.DataDir == "" {
   349  		if config.Client.AllocDir == "" || config.Client.StateDir == "" || config.PluginDir == "" {
   350  			c.Ui.Error("Must specify the state, alloc dir, and plugin dir if data-dir is omitted.")
   351  			return false
   352  		}
   353  	}
   354  
   355  	// Check the bootstrap flags
   356  	if config.Server.BootstrapExpect > 0 && !config.Server.Enabled {
   357  		c.Ui.Error("Bootstrap requires server mode to be enabled")
   358  		return false
   359  	}
   360  	if config.Server.BootstrapExpect == 1 {
   361  		c.Ui.Error("WARNING: Bootstrap mode enabled! Potentially unsafe operation.")
   362  	}
   363  
   364  	return true
   365  }
   366  
   367  // setupLoggers is used to setup the logGate, logWriter, and our logOutput
   368  func (c *Command) setupLoggers(config *Config) (*gatedwriter.Writer, *logWriter, io.Writer) {
   369  	// Setup logging. First create the gated log writer, which will
   370  	// store logs until we're ready to show them. Then create the level
   371  	// filter, filtering logs of the specified level.
   372  	logGate := &gatedwriter.Writer{
   373  		Writer: &cli.UiWriter{Ui: c.Ui},
   374  	}
   375  
   376  	c.logFilter = LevelFilter()
   377  	c.logFilter.MinLevel = logutils.LogLevel(strings.ToUpper(config.LogLevel))
   378  	c.logFilter.Writer = logGate
   379  	if !ValidateLevelFilter(c.logFilter.MinLevel, c.logFilter) {
   380  		c.Ui.Error(fmt.Sprintf(
   381  			"Invalid log level: %s. Valid log levels are: %v",
   382  			c.logFilter.MinLevel, c.logFilter.Levels))
   383  		return nil, nil, nil
   384  	}
   385  
   386  	// Check if syslog is enabled
   387  	var syslog io.Writer
   388  	if config.EnableSyslog {
   389  		l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, "nomad")
   390  		if err != nil {
   391  			c.Ui.Error(fmt.Sprintf("Syslog setup failed: %v", err))
   392  			return nil, nil, nil
   393  		}
   394  		syslog = &SyslogWrapper{l, c.logFilter}
   395  	}
   396  
   397  	// Create a log writer, and wrap a logOutput around it
   398  	logWriter := NewLogWriter(512)
   399  	var logOutput io.Writer
   400  	if syslog != nil {
   401  		logOutput = io.MultiWriter(c.logFilter, logWriter, syslog)
   402  	} else {
   403  		logOutput = io.MultiWriter(c.logFilter, logWriter)
   404  	}
   405  	c.logOutput = logOutput
   406  	log.SetOutput(logOutput)
   407  	return logGate, logWriter, logOutput
   408  }
   409  
   410  // setupAgent is used to start the agent and various interfaces
   411  func (c *Command) setupAgent(config *Config, logger hclog.Logger, logOutput io.Writer, inmem *metrics.InmemSink) error {
   412  	c.Ui.Output("Starting Nomad agent...")
   413  	agent, err := NewAgent(config, logger, logOutput, inmem)
   414  	if err != nil {
   415  		c.Ui.Error(fmt.Sprintf("Error starting agent: %s", err))
   416  		return err
   417  	}
   418  	c.agent = agent
   419  
   420  	// Setup the HTTP server
   421  	http, err := NewHTTPServer(agent, config)
   422  	if err != nil {
   423  		agent.Shutdown()
   424  		c.Ui.Error(fmt.Sprintf("Error starting http server: %s", err))
   425  		return err
   426  	}
   427  	c.httpServer = http
   428  
   429  	// If DisableUpdateCheck is not enabled, set up update checking
   430  	// (DisableUpdateCheck is false by default)
   431  	if config.DisableUpdateCheck != nil && !*config.DisableUpdateCheck {
   432  		version := config.Version.Version
   433  		if config.Version.VersionPrerelease != "" {
   434  			version += fmt.Sprintf("-%s", config.Version.VersionPrerelease)
   435  		}
   436  		updateParams := &checkpoint.CheckParams{
   437  			Product: "nomad",
   438  			Version: version,
   439  		}
   440  		if !config.DisableAnonymousSignature {
   441  			updateParams.SignatureFile = filepath.Join(config.DataDir, "checkpoint-signature")
   442  		}
   443  
   444  		// Schedule a periodic check with expected interval of 24 hours
   445  		checkpoint.CheckInterval(updateParams, 24*time.Hour, c.checkpointResults)
   446  
   447  		// Do an immediate check within the next 30 seconds
   448  		go func() {
   449  			time.Sleep(lib.RandomStagger(30 * time.Second))
   450  			c.checkpointResults(checkpoint.Check(updateParams))
   451  		}()
   452  	}
   453  
   454  	return nil
   455  }
   456  
   457  // checkpointResults is used to handler periodic results from our update checker
   458  func (c *Command) checkpointResults(results *checkpoint.CheckResponse, err error) {
   459  	if err != nil {
   460  		c.Ui.Error(fmt.Sprintf("Failed to check for updates: %v", err))
   461  		return
   462  	}
   463  	if results.Outdated {
   464  		c.Ui.Error(fmt.Sprintf("Newer Nomad version available: %s (currently running: %s)", results.CurrentVersion, c.Version.VersionNumber()))
   465  	}
   466  	for _, alert := range results.Alerts {
   467  		switch alert.Level {
   468  		case "info":
   469  			c.Ui.Info(fmt.Sprintf("Bulletin [%s]: %s (%s)", alert.Level, alert.Message, alert.URL))
   470  		default:
   471  			c.Ui.Error(fmt.Sprintf("Bulletin [%s]: %s (%s)", alert.Level, alert.Message, alert.URL))
   472  		}
   473  	}
   474  }
   475  
   476  func (c *Command) AutocompleteFlags() complete.Flags {
   477  	configFilePredictor := complete.PredictOr(
   478  		complete.PredictFiles("*.json"),
   479  		complete.PredictFiles("*.hcl"))
   480  
   481  	return map[string]complete.Predictor{
   482  		"-dev":                           complete.PredictNothing,
   483  		"-server":                        complete.PredictNothing,
   484  		"-client":                        complete.PredictNothing,
   485  		"-bootstrap-expect":              complete.PredictAnything,
   486  		"-encrypt":                       complete.PredictAnything,
   487  		"-raft-protocol":                 complete.PredictAnything,
   488  		"-rejoin":                        complete.PredictNothing,
   489  		"-join":                          complete.PredictAnything,
   490  		"-retry-join":                    complete.PredictAnything,
   491  		"-retry-max":                     complete.PredictAnything,
   492  		"-state-dir":                     complete.PredictDirs("*"),
   493  		"-alloc-dir":                     complete.PredictDirs("*"),
   494  		"-node-class":                    complete.PredictAnything,
   495  		"-servers":                       complete.PredictAnything,
   496  		"-meta":                          complete.PredictAnything,
   497  		"-config":                        configFilePredictor,
   498  		"-bind":                          complete.PredictAnything,
   499  		"-region":                        complete.PredictAnything,
   500  		"-data-dir":                      complete.PredictDirs("*"),
   501  		"-plugin-dir":                    complete.PredictDirs("*"),
   502  		"-dc":                            complete.PredictAnything,
   503  		"-log-level":                     complete.PredictAnything,
   504  		"-json-logs":                     complete.PredictNothing,
   505  		"-node":                          complete.PredictAnything,
   506  		"-consul-auth":                   complete.PredictAnything,
   507  		"-consul-auto-advertise":         complete.PredictNothing,
   508  		"-consul-ca-file":                complete.PredictAnything,
   509  		"-consul-cert-file":              complete.PredictAnything,
   510  		"-consul-key-file":               complete.PredictAnything,
   511  		"-consul-checks-use-advertise":   complete.PredictNothing,
   512  		"-consul-client-auto-join":       complete.PredictNothing,
   513  		"-consul-client-service-name":    complete.PredictAnything,
   514  		"-consul-client-http-check-name": complete.PredictAnything,
   515  		"-consul-server-service-name":    complete.PredictAnything,
   516  		"-consul-server-http-check-name": complete.PredictAnything,
   517  		"-consul-server-serf-check-name": complete.PredictAnything,
   518  		"-consul-server-rpc-check-name":  complete.PredictAnything,
   519  		"-consul-server-auto-join":       complete.PredictNothing,
   520  		"-consul-ssl":                    complete.PredictNothing,
   521  		"-consul-verify-ssl":             complete.PredictNothing,
   522  		"-consul-address":                complete.PredictAnything,
   523  		"-vault-enabled":                 complete.PredictNothing,
   524  		"-vault-allow-unauthenticated":   complete.PredictNothing,
   525  		"-vault-token":                   complete.PredictAnything,
   526  		"-vault-address":                 complete.PredictAnything,
   527  		"-vault-create-from-role":        complete.PredictAnything,
   528  		"-vault-ca-file":                 complete.PredictAnything,
   529  		"-vault-ca-path":                 complete.PredictAnything,
   530  		"-vault-cert-file":               complete.PredictAnything,
   531  		"-vault-key-file":                complete.PredictAnything,
   532  		"-vault-tls-skip-verify":         complete.PredictNothing,
   533  		"-vault-tls-server-name":         complete.PredictAnything,
   534  		"-acl-enabled":                   complete.PredictNothing,
   535  		"-acl-replication-token":         complete.PredictAnything,
   536  	}
   537  }
   538  
   539  func (c *Command) AutocompleteArgs() complete.Predictor {
   540  	return nil
   541  }
   542  
   543  func (c *Command) Run(args []string) int {
   544  	c.Ui = &cli.PrefixedUi{
   545  		OutputPrefix: "==> ",
   546  		InfoPrefix:   "    ",
   547  		ErrorPrefix:  "==> ",
   548  		Ui:           c.Ui,
   549  	}
   550  
   551  	// Parse our configs
   552  	c.args = args
   553  	config := c.readConfig()
   554  	if config == nil {
   555  		return 1
   556  	}
   557  
   558  	// Setup the log outputs
   559  	logGate, _, logOutput := c.setupLoggers(config)
   560  	if logGate == nil {
   561  		return 1
   562  	}
   563  
   564  	// Create logger
   565  	logger := hclog.New(&hclog.LoggerOptions{
   566  		Name:       "agent",
   567  		Level:      hclog.LevelFromString(config.LogLevel),
   568  		Output:     logOutput,
   569  		JSONFormat: config.LogJson,
   570  	})
   571  
   572  	// Swap out UI implementation if json logging is enabled
   573  	if config.LogJson {
   574  		c.Ui = &logging.HcLogUI{Log: logger}
   575  	}
   576  
   577  	// Log config files
   578  	if len(config.Files) > 0 {
   579  		c.Ui.Output(fmt.Sprintf("Loaded configuration from %s", strings.Join(config.Files, ", ")))
   580  	} else {
   581  		c.Ui.Output("No configuration files loaded")
   582  	}
   583  
   584  	// Initialize the telemetry
   585  	inmem, err := c.setupTelemetry(config)
   586  	if err != nil {
   587  		c.Ui.Error(fmt.Sprintf("Error initializing telemetry: %s", err))
   588  		return 1
   589  	}
   590  
   591  	// Create the agent
   592  	if err := c.setupAgent(config, logger, logOutput, inmem); err != nil {
   593  		logGate.Flush()
   594  		return 1
   595  	}
   596  	defer c.agent.Shutdown()
   597  
   598  	// Shutdown the HTTP server at the end
   599  	defer func() {
   600  		if c.httpServer != nil {
   601  			c.httpServer.Shutdown()
   602  		}
   603  	}()
   604  
   605  	// Join startup nodes if specified
   606  	if err := c.startupJoin(config); err != nil {
   607  		c.Ui.Error(err.Error())
   608  		return 1
   609  	}
   610  
   611  	// Compile agent information for output later
   612  	info := make(map[string]string)
   613  	info["version"] = config.Version.VersionNumber()
   614  	info["client"] = strconv.FormatBool(config.Client.Enabled)
   615  	info["log level"] = config.LogLevel
   616  	info["server"] = strconv.FormatBool(config.Server.Enabled)
   617  	info["region"] = fmt.Sprintf("%s (DC: %s)", config.Region, config.Datacenter)
   618  	info["bind addrs"] = c.getBindAddrSynopsis()
   619  	info["advertise addrs"] = c.getAdvertiseAddrSynopsis()
   620  
   621  	// Sort the keys for output
   622  	infoKeys := make([]string, 0, len(info))
   623  	for key := range info {
   624  		infoKeys = append(infoKeys, key)
   625  	}
   626  	sort.Strings(infoKeys)
   627  
   628  	// Agent configuration output
   629  	padding := 18
   630  	c.Ui.Output("Nomad agent configuration:\n")
   631  	for _, k := range infoKeys {
   632  		c.Ui.Info(fmt.Sprintf(
   633  			"%s%s: %s",
   634  			strings.Repeat(" ", padding-len(k)),
   635  			strings.Title(k),
   636  			info[k]))
   637  	}
   638  	c.Ui.Output("")
   639  
   640  	// Output the header that the server has started
   641  	c.Ui.Output("Nomad agent started! Log data will stream in below:\n")
   642  
   643  	// Enable log streaming
   644  	logGate.Flush()
   645  
   646  	// Start retry join process
   647  	if err := c.handleRetryJoin(config); err != nil {
   648  		c.Ui.Error(err.Error())
   649  		return 1
   650  	}
   651  
   652  	// Wait for exit
   653  	return c.handleSignals()
   654  }
   655  
   656  // handleRetryJoin is used to start retry joining if it is configured.
   657  func (c *Command) handleRetryJoin(config *Config) error {
   658  	c.retryJoinErrCh = make(chan struct{})
   659  
   660  	if config.Server.Enabled && len(config.Server.RetryJoin) != 0 {
   661  		joiner := retryJoiner{
   662  			discover:      &discover.Discover{},
   663  			errCh:         c.retryJoinErrCh,
   664  			logger:        c.agent.logger.Named("joiner"),
   665  			serverJoin:    c.agent.server.Join,
   666  			serverEnabled: true,
   667  		}
   668  
   669  		if err := joiner.Validate(config); err != nil {
   670  			return err
   671  		}
   672  
   673  		// Remove the duplicate fields
   674  		if len(config.Server.RetryJoin) != 0 {
   675  			config.Server.ServerJoin.RetryJoin = config.Server.RetryJoin
   676  			config.Server.RetryJoin = nil
   677  		}
   678  		if config.Server.RetryMaxAttempts != 0 {
   679  			config.Server.ServerJoin.RetryMaxAttempts = config.Server.RetryMaxAttempts
   680  			config.Server.RetryMaxAttempts = 0
   681  		}
   682  		if config.Server.RetryInterval != 0 {
   683  			config.Server.ServerJoin.RetryInterval = config.Server.RetryInterval
   684  			config.Server.RetryInterval = 0
   685  		}
   686  
   687  		c.agent.logger.Warn("using deprecated retry_join fields. Upgrade configuration to use server_join")
   688  	}
   689  
   690  	if config.Server.Enabled &&
   691  		config.Server.ServerJoin != nil &&
   692  		len(config.Server.ServerJoin.RetryJoin) != 0 {
   693  
   694  		joiner := retryJoiner{
   695  			discover:      &discover.Discover{},
   696  			errCh:         c.retryJoinErrCh,
   697  			logger:        c.agent.logger.Named("joiner"),
   698  			serverJoin:    c.agent.server.Join,
   699  			serverEnabled: true,
   700  		}
   701  
   702  		if err := joiner.Validate(config); err != nil {
   703  			return err
   704  		}
   705  
   706  		go joiner.RetryJoin(config.Server.ServerJoin)
   707  	}
   708  
   709  	if config.Client.Enabled &&
   710  		config.Client.ServerJoin != nil &&
   711  		len(config.Client.ServerJoin.RetryJoin) != 0 {
   712  		joiner := retryJoiner{
   713  			discover:      &discover.Discover{},
   714  			errCh:         c.retryJoinErrCh,
   715  			logger:        c.agent.logger.Named("joiner"),
   716  			clientJoin:    c.agent.client.SetServers,
   717  			clientEnabled: true,
   718  		}
   719  
   720  		if err := joiner.Validate(config); err != nil {
   721  			return err
   722  		}
   723  
   724  		go joiner.RetryJoin(config.Client.ServerJoin)
   725  	}
   726  
   727  	return nil
   728  }
   729  
   730  // handleSignals blocks until we get an exit-causing signal
   731  func (c *Command) handleSignals() int {
   732  	signalCh := make(chan os.Signal, 4)
   733  	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE)
   734  
   735  	// Wait for a signal
   736  WAIT:
   737  	var sig os.Signal
   738  	select {
   739  	case s := <-signalCh:
   740  		sig = s
   741  	case <-c.ShutdownCh:
   742  		sig = os.Interrupt
   743  	case <-c.retryJoinErrCh:
   744  		return 1
   745  	}
   746  
   747  	// Skip any SIGPIPE signal and don't try to log it (See issues #1798, #3554)
   748  	if sig == syscall.SIGPIPE {
   749  		goto WAIT
   750  	}
   751  
   752  	c.Ui.Output(fmt.Sprintf("Caught signal: %v", sig))
   753  
   754  	// Check if this is a SIGHUP
   755  	if sig == syscall.SIGHUP {
   756  		c.handleReload()
   757  		goto WAIT
   758  	}
   759  
   760  	// Check if we should do a graceful leave
   761  	graceful := false
   762  	if sig == os.Interrupt && c.agent.GetConfig().LeaveOnInt {
   763  		graceful = true
   764  	} else if sig == syscall.SIGTERM && c.agent.GetConfig().LeaveOnTerm {
   765  		graceful = true
   766  	}
   767  
   768  	// Bail fast if not doing a graceful leave
   769  	if !graceful {
   770  		return 1
   771  	}
   772  
   773  	// Attempt a graceful leave
   774  	gracefulCh := make(chan struct{})
   775  	c.Ui.Output("Gracefully shutting down agent...")
   776  	go func() {
   777  		if err := c.agent.Leave(); err != nil {
   778  			c.Ui.Error(fmt.Sprintf("Error: %s", err))
   779  			return
   780  		}
   781  		close(gracefulCh)
   782  	}()
   783  
   784  	// Wait for leave or another signal
   785  	select {
   786  	case <-signalCh:
   787  		return 1
   788  	case <-time.After(gracefulTimeout):
   789  		return 1
   790  	case <-gracefulCh:
   791  		return 0
   792  	}
   793  }
   794  
   795  // reloadHTTPServer shuts down the existing HTTP server and restarts it. This
   796  // is helpful when reloading the agent configuration.
   797  func (c *Command) reloadHTTPServer() error {
   798  	c.agent.logger.Info("reloading HTTP server with new TLS configuration")
   799  
   800  	c.httpServer.Shutdown()
   801  
   802  	http, err := NewHTTPServer(c.agent, c.agent.config)
   803  	if err != nil {
   804  		return err
   805  	}
   806  	c.httpServer = http
   807  
   808  	return nil
   809  }
   810  
   811  // handleReload is invoked when we should reload our configs, e.g. SIGHUP
   812  func (c *Command) handleReload() {
   813  	c.Ui.Output("Reloading configuration...")
   814  	newConf := c.readConfig()
   815  	if newConf == nil {
   816  		c.Ui.Error(fmt.Sprintf("Failed to reload configs"))
   817  		return
   818  	}
   819  
   820  	// Change the log level
   821  	minLevel := logutils.LogLevel(strings.ToUpper(newConf.LogLevel))
   822  	if ValidateLevelFilter(minLevel, c.logFilter) {
   823  		c.logFilter.SetMinLevel(minLevel)
   824  	} else {
   825  		c.Ui.Error(fmt.Sprintf(
   826  			"Invalid log level: %s. Valid log levels are: %v",
   827  			minLevel, c.logFilter.Levels))
   828  
   829  		// Keep the current log level
   830  		newConf.LogLevel = c.agent.GetConfig().LogLevel
   831  	}
   832  
   833  	shouldReloadAgent, shouldReloadHTTP := c.agent.ShouldReload(newConf)
   834  	if shouldReloadAgent {
   835  		c.agent.logger.Debug("starting reload of agent config")
   836  		err := c.agent.Reload(newConf)
   837  		if err != nil {
   838  			c.agent.logger.Error("failed to reload the config", "error", err)
   839  			return
   840  		}
   841  	}
   842  
   843  	if s := c.agent.Server(); s != nil {
   844  		c.agent.logger.Debug("starting reload of server config")
   845  		sconf, err := convertServerConfig(newConf)
   846  		if err != nil {
   847  			c.agent.logger.Error("failed to convert server config", "error", err)
   848  			return
   849  		}
   850  
   851  		// Finalize the config to get the agent objects injected in
   852  		c.agent.finalizeServerConfig(sconf)
   853  
   854  		// Reload the config
   855  		if err := s.Reload(sconf); err != nil {
   856  			c.agent.logger.Error("reloading server config failed", "error", err)
   857  			return
   858  		}
   859  	}
   860  
   861  	if s := c.agent.Client(); s != nil {
   862  		c.agent.logger.Debug("starting reload of client config")
   863  		clientConfig, err := convertClientConfig(newConf)
   864  		if err != nil {
   865  			c.agent.logger.Error("failed to convert client config", "error", err)
   866  			return
   867  		}
   868  
   869  		// Finalize the config to get the agent objects injected in
   870  		if err := c.agent.finalizeClientConfig(clientConfig); err != nil {
   871  			c.agent.logger.Error("failed to finalize client config", "error", err)
   872  			return
   873  		}
   874  
   875  		if err := c.agent.Client().Reload(clientConfig); err != nil {
   876  			c.agent.logger.Error("reloading client config failed", "error", err)
   877  			return
   878  		}
   879  	}
   880  
   881  	// reload HTTP server after we have reloaded both client and server, in case
   882  	// we error in either of the above cases. For example, reloading the http
   883  	// server to a TLS connection could succeed, while reloading the server's rpc
   884  	// connections could fail.
   885  	if shouldReloadHTTP {
   886  		err := c.reloadHTTPServer()
   887  		if err != nil {
   888  			c.agent.httpLogger.Error("reloading config failed", "error", err)
   889  			return
   890  		}
   891  	}
   892  }
   893  
   894  // setupTelemetry is used ot setup the telemetry sub-systems
   895  func (c *Command) setupTelemetry(config *Config) (*metrics.InmemSink, error) {
   896  	/* Setup telemetry
   897  	Aggregate on 10 second intervals for 1 minute. Expose the
   898  	metrics over stderr when there is a SIGUSR1 received.
   899  	*/
   900  	inm := metrics.NewInmemSink(10*time.Second, time.Minute)
   901  	metrics.DefaultInmemSignal(inm)
   902  
   903  	var telConfig *Telemetry
   904  	if config.Telemetry == nil {
   905  		telConfig = &Telemetry{}
   906  	} else {
   907  		telConfig = config.Telemetry
   908  	}
   909  
   910  	metricsConf := metrics.DefaultConfig("nomad")
   911  	metricsConf.EnableHostname = !telConfig.DisableHostname
   912  
   913  	// Prefer the hostname as a label.
   914  	metricsConf.EnableHostnameLabel = !telConfig.DisableHostname &&
   915  		!telConfig.DisableTaggedMetrics && !telConfig.BackwardsCompatibleMetrics
   916  
   917  	if telConfig.UseNodeName {
   918  		metricsConf.HostName = config.NodeName
   919  		metricsConf.EnableHostname = true
   920  	}
   921  
   922  	allowedPrefixes, blockedPrefixes, err := telConfig.PrefixFilters()
   923  	if err != nil {
   924  		return inm, err
   925  	}
   926  
   927  	metricsConf.AllowedPrefixes = allowedPrefixes
   928  	metricsConf.BlockedPrefixes = blockedPrefixes
   929  
   930  	if telConfig.FilterDefault != nil {
   931  		metricsConf.FilterDefault = *telConfig.FilterDefault
   932  	}
   933  
   934  	// Configure the statsite sink
   935  	var fanout metrics.FanoutSink
   936  	if telConfig.StatsiteAddr != "" {
   937  		sink, err := metrics.NewStatsiteSink(telConfig.StatsiteAddr)
   938  		if err != nil {
   939  			return inm, err
   940  		}
   941  		fanout = append(fanout, sink)
   942  	}
   943  
   944  	// Configure the statsd sink
   945  	if telConfig.StatsdAddr != "" {
   946  		sink, err := metrics.NewStatsdSink(telConfig.StatsdAddr)
   947  		if err != nil {
   948  			return inm, err
   949  		}
   950  		fanout = append(fanout, sink)
   951  	}
   952  
   953  	// Configure the prometheus sink
   954  	if telConfig.PrometheusMetrics {
   955  		promSink, err := prometheus.NewPrometheusSink()
   956  		if err != nil {
   957  			return inm, err
   958  		}
   959  		fanout = append(fanout, promSink)
   960  	}
   961  
   962  	// Configure the datadog sink
   963  	if telConfig.DataDogAddr != "" {
   964  		sink, err := datadog.NewDogStatsdSink(telConfig.DataDogAddr, config.NodeName)
   965  		if err != nil {
   966  			return inm, err
   967  		}
   968  		sink.SetTags(telConfig.DataDogTags)
   969  		fanout = append(fanout, sink)
   970  	}
   971  
   972  	// Configure the Circonus sink
   973  	if telConfig.CirconusAPIToken != "" || telConfig.CirconusCheckSubmissionURL != "" {
   974  		cfg := &circonus.Config{}
   975  		cfg.Interval = telConfig.CirconusSubmissionInterval
   976  		cfg.CheckManager.API.TokenKey = telConfig.CirconusAPIToken
   977  		cfg.CheckManager.API.TokenApp = telConfig.CirconusAPIApp
   978  		cfg.CheckManager.API.URL = telConfig.CirconusAPIURL
   979  		cfg.CheckManager.Check.SubmissionURL = telConfig.CirconusCheckSubmissionURL
   980  		cfg.CheckManager.Check.ID = telConfig.CirconusCheckID
   981  		cfg.CheckManager.Check.ForceMetricActivation = telConfig.CirconusCheckForceMetricActivation
   982  		cfg.CheckManager.Check.InstanceID = telConfig.CirconusCheckInstanceID
   983  		cfg.CheckManager.Check.SearchTag = telConfig.CirconusCheckSearchTag
   984  		cfg.CheckManager.Check.Tags = telConfig.CirconusCheckTags
   985  		cfg.CheckManager.Check.DisplayName = telConfig.CirconusCheckDisplayName
   986  		cfg.CheckManager.Broker.ID = telConfig.CirconusBrokerID
   987  		cfg.CheckManager.Broker.SelectTag = telConfig.CirconusBrokerSelectTag
   988  
   989  		if cfg.CheckManager.Check.DisplayName == "" {
   990  			cfg.CheckManager.Check.DisplayName = "Nomad"
   991  		}
   992  
   993  		if cfg.CheckManager.API.TokenApp == "" {
   994  			cfg.CheckManager.API.TokenApp = "nomad"
   995  		}
   996  
   997  		if cfg.CheckManager.Check.SearchTag == "" {
   998  			cfg.CheckManager.Check.SearchTag = "service:nomad"
   999  		}
  1000  
  1001  		sink, err := circonus.NewCirconusSink(cfg)
  1002  		if err != nil {
  1003  			return inm, err
  1004  		}
  1005  		sink.Start()
  1006  		fanout = append(fanout, sink)
  1007  	}
  1008  
  1009  	// Initialize the global sink
  1010  	if len(fanout) > 0 {
  1011  		fanout = append(fanout, inm)
  1012  		metrics.NewGlobal(metricsConf, fanout)
  1013  	} else {
  1014  		metricsConf.EnableHostname = false
  1015  		metrics.NewGlobal(metricsConf, inm)
  1016  	}
  1017  
  1018  	return inm, nil
  1019  }
  1020  
  1021  func (c *Command) startupJoin(config *Config) error {
  1022  	// Nothing to do
  1023  	if !config.Server.Enabled {
  1024  		return nil
  1025  	}
  1026  
  1027  	// Validate both old and new aren't being set
  1028  	old := len(config.Server.StartJoin)
  1029  	var new int
  1030  	if config.Server.ServerJoin != nil {
  1031  		new = len(config.Server.ServerJoin.StartJoin)
  1032  	}
  1033  	if old != 0 && new != 0 {
  1034  		return fmt.Errorf("server_join and start_join cannot both be defined; prefer setting the server_join stanza")
  1035  	}
  1036  
  1037  	// Nothing to do
  1038  	if old+new == 0 {
  1039  		return nil
  1040  	}
  1041  
  1042  	// Combine the lists and join
  1043  	joining := config.Server.StartJoin
  1044  	if new != 0 {
  1045  		joining = append(joining, config.Server.ServerJoin.StartJoin...)
  1046  	}
  1047  
  1048  	c.Ui.Output("Joining cluster...")
  1049  	n, err := c.agent.server.Join(joining)
  1050  	if err != nil {
  1051  		return err
  1052  	}
  1053  
  1054  	c.Ui.Output(fmt.Sprintf("Join completed. Synced with %d initial agents", n))
  1055  	return nil
  1056  }
  1057  
  1058  // getBindAddrSynopsis returns a string that describes the addresses the agent
  1059  // is bound to.
  1060  func (c *Command) getBindAddrSynopsis() string {
  1061  	if c == nil || c.agent == nil || c.agent.config == nil || c.agent.config.normalizedAddrs == nil {
  1062  		return ""
  1063  	}
  1064  
  1065  	b := new(strings.Builder)
  1066  	fmt.Fprintf(b, "HTTP: %s", c.agent.config.normalizedAddrs.HTTP)
  1067  
  1068  	if c.agent.server != nil {
  1069  		if c.agent.config.normalizedAddrs.RPC != "" {
  1070  			fmt.Fprintf(b, "; RPC: %s", c.agent.config.normalizedAddrs.RPC)
  1071  		}
  1072  		if c.agent.config.normalizedAddrs.Serf != "" {
  1073  			fmt.Fprintf(b, "; Serf: %s", c.agent.config.normalizedAddrs.Serf)
  1074  		}
  1075  	}
  1076  
  1077  	return b.String()
  1078  }
  1079  
  1080  // getAdvertiseAddrSynopsis returns a string that describes the addresses the agent
  1081  // is advertising.
  1082  func (c *Command) getAdvertiseAddrSynopsis() string {
  1083  	if c == nil || c.agent == nil || c.agent.config == nil || c.agent.config.AdvertiseAddrs == nil {
  1084  		return ""
  1085  	}
  1086  
  1087  	b := new(strings.Builder)
  1088  	fmt.Fprintf(b, "HTTP: %s", c.agent.config.AdvertiseAddrs.HTTP)
  1089  
  1090  	if c.agent.server != nil {
  1091  		if c.agent.config.AdvertiseAddrs.RPC != "" {
  1092  			fmt.Fprintf(b, "; RPC: %s", c.agent.config.AdvertiseAddrs.RPC)
  1093  		}
  1094  		if c.agent.config.AdvertiseAddrs.Serf != "" {
  1095  			fmt.Fprintf(b, "; Serf: %s", c.agent.config.AdvertiseAddrs.Serf)
  1096  		}
  1097  	}
  1098  
  1099  	return b.String()
  1100  }
  1101  
  1102  func (c *Command) Synopsis() string {
  1103  	return "Runs a Nomad agent"
  1104  }
  1105  
  1106  func (c *Command) Help() string {
  1107  	helpText := `
  1108  Usage: nomad agent [options]
  1109  
  1110    Starts the Nomad agent and runs until an interrupt is received.
  1111    The agent may be a client and/or server.
  1112  
  1113    The Nomad agent's configuration primarily comes from the config
  1114    files used, but a subset of the options may also be passed directly
  1115    as CLI arguments, listed below.
  1116  
  1117  General Options (clients and servers):
  1118  
  1119    -bind=<addr>
  1120      The address the agent will bind to for all of its various network
  1121      services. The individual services that run bind to individual
  1122      ports on this address. Defaults to the loopback 127.0.0.1.
  1123  
  1124    -config=<path>
  1125      The path to either a single config file or a directory of config
  1126      files to use for configuring the Nomad agent. This option may be
  1127      specified multiple times. If multiple config files are used, the
  1128      values from each will be merged together. During merging, values
  1129      from files found later in the list are merged over values from
  1130      previously parsed files.
  1131  
  1132    -data-dir=<path>
  1133      The data directory used to store state and other persistent data.
  1134      On client machines this is used to house allocation data such as
  1135      downloaded artifacts used by drivers. On server nodes, the data
  1136      dir is also used to store the replicated log.
  1137  
  1138    -plugin-dir=<path>
  1139      The plugin directory is used to discover Nomad plugins. If not specified,
  1140      the plugin directory defaults to be that of <data-dir>/plugins/.
  1141  
  1142    -dc=<datacenter>
  1143      The name of the datacenter this Nomad agent is a member of. By
  1144      default this is set to "dc1".
  1145  
  1146    -log-level=<level>
  1147      Specify the verbosity level of Nomad's logs. Valid values include
  1148      DEBUG, INFO, and WARN, in decreasing order of verbosity. The
  1149      default is INFO.
  1150  
  1151    -log-json
  1152      Output logs in a JSON format. The default is false.
  1153  
  1154    -node=<name>
  1155      The name of the local agent. This name is used to identify the node
  1156      in the cluster. The name must be unique per region. The default is
  1157      the current hostname of the machine.
  1158  
  1159    -region=<region>
  1160      Name of the region the Nomad agent will be a member of. By default
  1161      this value is set to "global".
  1162  
  1163    -dev
  1164      Start the agent in development mode. This enables a pre-configured
  1165      dual-role agent (client + server) which is useful for developing
  1166      or testing Nomad. No other configuration is required to start the
  1167      agent in this mode.
  1168  
  1169  Server Options:
  1170  
  1171    -server
  1172      Enable server mode for the agent. Agents in server mode are
  1173      clustered together and handle the additional responsibility of
  1174      leader election, data replication, and scheduling work onto
  1175      eligible client nodes.
  1176  
  1177    -bootstrap-expect=<num>
  1178      Configures the expected number of servers nodes to wait for before
  1179      bootstrapping the cluster. Once <num> servers have joined each other,
  1180      Nomad initiates the bootstrap process.
  1181  
  1182    -encrypt=<key>
  1183      Provides the gossip encryption key
  1184  
  1185    -join=<address>
  1186      Address of an agent to join at start time. Can be specified
  1187      multiple times.
  1188  
  1189    -raft-protocol=<num>
  1190      The Raft protocol version to use. Used for enabling certain Autopilot
  1191      features. Defaults to 2.
  1192  
  1193    -retry-join=<address>
  1194      Address of an agent to join at start time with retries enabled.
  1195      Can be specified multiple times.
  1196  
  1197    -retry-max=<num>
  1198      Maximum number of join attempts. Defaults to 0, which will retry
  1199      indefinitely.
  1200  
  1201    -retry-interval=<dur>
  1202      Time to wait between join attempts.
  1203  
  1204    -rejoin
  1205      Ignore a previous leave and attempts to rejoin the cluster.
  1206  
  1207  Client Options:
  1208  
  1209    -client
  1210      Enable client mode for the agent. Client mode enables a given node to be
  1211      evaluated for allocations. If client mode is not enabled, no work will be
  1212      scheduled to the agent.
  1213  
  1214    -state-dir
  1215      The directory used to store state and other persistent data. If not
  1216      specified a subdirectory under the "-data-dir" will be used.
  1217  
  1218    -alloc-dir
  1219      The directory used to store allocation data such as downloaded artifacts as
  1220      well as data produced by tasks. If not specified, a subdirectory under the
  1221      "-data-dir" will be used.
  1222  
  1223    -servers
  1224      A list of known server addresses to connect to given as "host:port" and
  1225      delimited by commas.
  1226  
  1227    -node-class
  1228      Mark this node as a member of a node-class. This can be used to label
  1229      similar node types.
  1230  
  1231    -meta
  1232      User specified metadata to associated with the node. Each instance of -meta
  1233      parses a single KEY=VALUE pair. Repeat the meta flag for each key/value pair
  1234      to be added.
  1235  
  1236    -network-interface
  1237      Forces the network fingerprinter to use the specified network interface.
  1238  
  1239    -network-speed
  1240      The default speed for network interfaces in MBits if the link speed can not
  1241      be determined dynamically.
  1242  
  1243  ACL Options:
  1244  
  1245    -acl-enabled
  1246      Specifies whether the agent should enable ACLs.
  1247  
  1248    -acl-replication-token
  1249      The replication token for servers to use when replicating from the
  1250      authoritative region. The token must be a valid management token from the
  1251      authoritative region.
  1252  
  1253  Consul Options:
  1254  
  1255    -consul-address=<addr>
  1256      Specifies the address to the local Consul agent, given in the format host:port.
  1257      Supports Unix sockets with the format: unix:///tmp/consul/consul.sock
  1258  
  1259    -consul-auth=<auth>
  1260      Specifies the HTTP Basic Authentication information to use for access to the
  1261      Consul Agent, given in the format username:password.
  1262  
  1263    -consul-auto-advertise
  1264      Specifies if Nomad should advertise its services in Consul. The services
  1265      are named according to server_service_name and client_service_name. Nomad
  1266      servers and clients advertise their respective services, each tagged
  1267      appropriately with either http or rpc tag. Nomad servers also advertise a
  1268      serf tagged service.
  1269  
  1270    -consul-ca-file=<path>
  1271      Specifies an optional path to the CA certificate used for Consul communication.
  1272      This defaults to the system bundle if unspecified.
  1273  
  1274    -consul-cert-file=<path>
  1275      Specifies the path to the certificate used for Consul communication. If this
  1276      is set then you need to also set key_file.
  1277  
  1278    -consul-checks-use-advertise
  1279      Specifies if Consul heath checks should bind to the advertise address. By
  1280      default, this is the bind address.
  1281  
  1282    -consul-client-auto-join
  1283      Specifies if the Nomad clients should automatically discover servers in the
  1284      same region by searching for the Consul service name defined in the
  1285      server_service_name option.
  1286  
  1287    -consul-client-service-name=<name>
  1288      Specifies the name of the service in Consul for the Nomad clients.
  1289  
  1290    -consul-client-http-check-name=<name>
  1291      Specifies the HTTP health check name in Consul for the Nomad clients.
  1292  
  1293    -consul-key-file=<path>
  1294      Specifies the path to the private key used for Consul communication. If this
  1295      is set then you need to also set cert_file.
  1296  
  1297    -consul-server-service-name=<name>
  1298      Specifies the name of the service in Consul for the Nomad servers.
  1299  
  1300    -consul-server-http-check-name=<name>
  1301      Specifies the HTTP health check name in Consul for the Nomad servers.
  1302  
  1303    -consul-server-serf-check-name=<name>
  1304      Specifies the Serf health check name in Consul for the Nomad servers.
  1305  
  1306    -consul-server-rpc-check-name=<name>
  1307      Specifies the RPC health check name in Consul for the Nomad servers.
  1308  
  1309    -consul-server-auto-join
  1310      Specifies if the Nomad servers should automatically discover and join other
  1311      Nomad servers by searching for the Consul service name defined in the
  1312      server_service_name option. This search only happens if the server does not
  1313      have a leader.
  1314  
  1315    -consul-ssl
  1316      Specifies if the transport scheme should use HTTPS to communicate with the
  1317      Consul agent.
  1318  
  1319    -consul-token=<token>
  1320      Specifies the token used to provide a per-request ACL token.
  1321  
  1322    -consul-verify-ssl
  1323      Specifies if SSL peer verification should be used when communicating to the
  1324      Consul API client over HTTPS.
  1325  
  1326  Vault Options:
  1327  
  1328    -vault-enabled
  1329      Whether to enable or disable Vault integration.
  1330  
  1331    -vault-address=<addr>
  1332      The address to communicate with Vault. This should be provided with the http://
  1333      or https:// prefix.
  1334  
  1335    -vault-token=<token>
  1336      The Vault token used to derive tokens from Vault on behalf of clients.
  1337      This only needs to be set on Servers. Overrides the Vault token read from
  1338      the VAULT_TOKEN environment variable.
  1339  
  1340    -vault-create-from-role=<role>
  1341      The role name to create tokens for tasks from.
  1342  
  1343    -vault-allow-unauthenticated
  1344      Whether to allow jobs to be submitted that request Vault Tokens but do not
  1345      authentication. The flag only applies to Servers.
  1346  
  1347    -vault-ca-file=<path>
  1348      The path to a PEM-encoded CA cert file to use to verify the Vault server SSL
  1349      certificate.
  1350  
  1351    -vault-ca-path=<path>
  1352      The path to a directory of PEM-encoded CA cert files to verify the Vault server
  1353      certificate.
  1354  
  1355    -vault-cert-file=<token>
  1356      The path to the certificate for Vault communication.
  1357  
  1358    -vault-key-file=<addr>
  1359      The path to the private key for Vault communication.
  1360  
  1361    -vault-tls-skip-verify=<token>
  1362      Enables or disables SSL certificate verification.
  1363  
  1364    -vault-tls-server-name=<token>
  1365      Used to set the SNI host when connecting over TLS.
  1366   `
  1367  	return strings.TrimSpace(helpText)
  1368  }