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