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