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