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