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