github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/command/agent/agent.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "net" 8 "os" 9 "path/filepath" 10 "runtime" 11 "strconv" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "time" 16 17 "github.com/hashicorp/nomad/client" 18 clientconfig "github.com/hashicorp/nomad/client/config" 19 "github.com/hashicorp/nomad/command/agent/consul" 20 "github.com/hashicorp/nomad/nomad" 21 "github.com/hashicorp/nomad/nomad/structs" 22 ) 23 24 const ( 25 clientHttpCheckInterval = 10 * time.Second 26 clientHttpCheckTimeout = 3 * time.Second 27 serverHttpCheckInterval = 10 * time.Second 28 serverHttpCheckTimeout = 6 * time.Second 29 serverRpcCheckInterval = 10 * time.Second 30 serverRpcCheckTimeout = 3 * time.Second 31 serverSerfCheckInterval = 10 * time.Second 32 serverSerfCheckTimeout = 3 * time.Second 33 ) 34 35 // Agent is a long running daemon that is used to run both 36 // clients and servers. Servers are responsible for managing 37 // state and making scheduling decisions. Clients can be 38 // scheduled to, and are responsible for interfacing with 39 // servers to run allocations. 40 type Agent struct { 41 config *Config 42 logger *log.Logger 43 logOutput io.Writer 44 45 // consulSyncer registers the Nomad agent with the Consul Agent 46 consulSyncer *consul.Syncer 47 48 client *client.Client 49 50 server *nomad.Server 51 52 shutdown bool 53 shutdownCh chan struct{} 54 shutdownLock sync.Mutex 55 } 56 57 // NewAgent is used to create a new agent with the given configuration 58 func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) { 59 a := &Agent{ 60 config: config, 61 logger: log.New(logOutput, "", log.LstdFlags|log.Lmicroseconds), 62 logOutput: logOutput, 63 shutdownCh: make(chan struct{}), 64 } 65 66 if err := a.setupConsulSyncer(); err != nil { 67 return nil, fmt.Errorf("Failed to initialize Consul syncer task: %v", err) 68 } 69 if err := a.setupServer(); err != nil { 70 return nil, err 71 } 72 if err := a.setupClient(); err != nil { 73 return nil, err 74 } 75 if a.client == nil && a.server == nil { 76 return nil, fmt.Errorf("must have at least client or server mode enabled") 77 } 78 79 // The Nomad Agent runs the consul.Syncer regardless of whether or not the 80 // Agent is running in Client or Server mode (or both), and regardless of 81 // the consul.auto_advertise parameter. The Client and Server both reuse the 82 // same consul.Syncer instance. This Syncer task periodically executes 83 // callbacks that update Consul. The reason the Syncer is always running is 84 // because one of the callbacks is attempts to self-bootstrap Nomad using 85 // information found in Consul. 86 go a.consulSyncer.Run() 87 88 return a, nil 89 } 90 91 // convertServerConfig takes an agent config and log output and returns a Nomad 92 // Config. 93 func convertServerConfig(agentConfig *Config, logOutput io.Writer) (*nomad.Config, error) { 94 conf := agentConfig.NomadConfig 95 if conf == nil { 96 conf = nomad.DefaultConfig() 97 } 98 conf.LogOutput = logOutput 99 conf.DevMode = agentConfig.DevMode 100 conf.Build = fmt.Sprintf("%s%s", agentConfig.Version, agentConfig.VersionPrerelease) 101 if agentConfig.Region != "" { 102 conf.Region = agentConfig.Region 103 } 104 if agentConfig.Datacenter != "" { 105 conf.Datacenter = agentConfig.Datacenter 106 } 107 if agentConfig.NodeName != "" { 108 conf.NodeName = agentConfig.NodeName 109 } 110 if agentConfig.Server.BootstrapExpect > 0 { 111 if agentConfig.Server.BootstrapExpect == 1 { 112 conf.Bootstrap = true 113 } else { 114 atomic.StoreInt32(&conf.BootstrapExpect, int32(agentConfig.Server.BootstrapExpect)) 115 } 116 } 117 if agentConfig.DataDir != "" { 118 conf.DataDir = filepath.Join(agentConfig.DataDir, "server") 119 } 120 if agentConfig.Server.DataDir != "" { 121 conf.DataDir = agentConfig.Server.DataDir 122 } 123 if agentConfig.Server.ProtocolVersion != 0 { 124 conf.ProtocolVersion = uint8(agentConfig.Server.ProtocolVersion) 125 } 126 if agentConfig.Server.NumSchedulers != 0 { 127 conf.NumSchedulers = agentConfig.Server.NumSchedulers 128 } 129 if len(agentConfig.Server.EnabledSchedulers) != 0 { 130 conf.EnabledSchedulers = agentConfig.Server.EnabledSchedulers 131 } 132 133 // Set up the bind addresses 134 rpcAddr, err := net.ResolveTCPAddr("tcp", agentConfig.normalizedAddrs.RPC) 135 if err != nil { 136 return nil, fmt.Errorf("Failed to parse RPC address %q: %v", agentConfig.normalizedAddrs.RPC, err) 137 } 138 serfAddr, err := net.ResolveTCPAddr("tcp", agentConfig.normalizedAddrs.Serf) 139 if err != nil { 140 return nil, fmt.Errorf("Failed to parse Serf address %q: %v", agentConfig.normalizedAddrs.Serf, err) 141 } 142 conf.RPCAddr.Port = rpcAddr.Port 143 conf.RPCAddr.IP = rpcAddr.IP 144 conf.SerfConfig.MemberlistConfig.BindPort = serfAddr.Port 145 conf.SerfConfig.MemberlistConfig.BindAddr = serfAddr.IP.String() 146 147 // Set up the advertise addresses 148 rpcAddr, err = net.ResolveTCPAddr("tcp", agentConfig.AdvertiseAddrs.RPC) 149 if err != nil { 150 return nil, fmt.Errorf("Failed to parse RPC advertise address %q: %v", agentConfig.AdvertiseAddrs.RPC, err) 151 } 152 serfAddr, err = net.ResolveTCPAddr("tcp", agentConfig.AdvertiseAddrs.Serf) 153 if err != nil { 154 return nil, fmt.Errorf("Failed to parse Serf advertise address %q: %v", agentConfig.AdvertiseAddrs.Serf, err) 155 } 156 conf.RPCAdvertise = rpcAddr 157 conf.SerfConfig.MemberlistConfig.AdvertiseAddr = serfAddr.IP.String() 158 conf.SerfConfig.MemberlistConfig.AdvertisePort = serfAddr.Port 159 160 // Set up gc threshold and heartbeat grace period 161 if gcThreshold := agentConfig.Server.NodeGCThreshold; gcThreshold != "" { 162 dur, err := time.ParseDuration(gcThreshold) 163 if err != nil { 164 return nil, err 165 } 166 conf.NodeGCThreshold = dur 167 } 168 if gcThreshold := agentConfig.Server.JobGCThreshold; gcThreshold != "" { 169 dur, err := time.ParseDuration(gcThreshold) 170 if err != nil { 171 return nil, err 172 } 173 conf.JobGCThreshold = dur 174 } 175 if gcThreshold := agentConfig.Server.EvalGCThreshold; gcThreshold != "" { 176 dur, err := time.ParseDuration(gcThreshold) 177 if err != nil { 178 return nil, err 179 } 180 conf.EvalGCThreshold = dur 181 } 182 183 if heartbeatGrace := agentConfig.Server.HeartbeatGrace; heartbeatGrace != "" { 184 dur, err := time.ParseDuration(heartbeatGrace) 185 if err != nil { 186 return nil, err 187 } 188 conf.HeartbeatGrace = dur 189 } 190 191 if *agentConfig.Consul.AutoAdvertise && agentConfig.Consul.ServerServiceName == "" { 192 return nil, fmt.Errorf("server_service_name must be set when auto_advertise is enabled") 193 } 194 195 // Add the Consul and Vault configs 196 conf.ConsulConfig = agentConfig.Consul 197 conf.VaultConfig = agentConfig.Vault 198 199 // Set the TLS config 200 conf.TLSConfig = agentConfig.TLSConfig 201 202 return conf, nil 203 } 204 205 // serverConfig is used to generate a new server configuration struct 206 // for initializing a nomad server. 207 func (a *Agent) serverConfig() (*nomad.Config, error) { 208 return convertServerConfig(a.config, a.logOutput) 209 } 210 211 // clientConfig is used to generate a new client configuration struct 212 // for initializing a Nomad client. 213 func (a *Agent) clientConfig() (*clientconfig.Config, error) { 214 // Setup the configuration 215 conf := a.config.ClientConfig 216 if conf == nil { 217 conf = clientconfig.DefaultConfig() 218 } 219 if a.server != nil { 220 conf.RPCHandler = a.server 221 } 222 conf.LogOutput = a.logOutput 223 conf.LogLevel = a.config.LogLevel 224 conf.DevMode = a.config.DevMode 225 if a.config.Region != "" { 226 conf.Region = a.config.Region 227 } 228 if a.config.DataDir != "" { 229 conf.StateDir = filepath.Join(a.config.DataDir, "client") 230 conf.AllocDir = filepath.Join(a.config.DataDir, "alloc") 231 } 232 if a.config.Client.StateDir != "" { 233 conf.StateDir = a.config.Client.StateDir 234 } 235 if a.config.Client.AllocDir != "" { 236 conf.AllocDir = a.config.Client.AllocDir 237 } 238 conf.Servers = a.config.Client.Servers 239 if a.config.Client.NetworkInterface != "" { 240 conf.NetworkInterface = a.config.Client.NetworkInterface 241 } 242 conf.ChrootEnv = a.config.Client.ChrootEnv 243 conf.Options = a.config.Client.Options 244 // Logging deprecation messages about consul related configuration in client 245 // options 246 var invalidConsulKeys []string 247 for key := range conf.Options { 248 if strings.HasPrefix(key, "consul") { 249 invalidConsulKeys = append(invalidConsulKeys, fmt.Sprintf("options.%s", key)) 250 } 251 } 252 if len(invalidConsulKeys) > 0 { 253 a.logger.Printf("[WARN] agent: Invalid keys: %v", strings.Join(invalidConsulKeys, ",")) 254 a.logger.Printf(`Nomad client ignores consul related configuration in client options. 255 Please refer to the guide https://www.nomadproject.io/docs/agent/configuration/consul.html 256 to configure Nomad to work with Consul.`) 257 } 258 259 if a.config.Client.NetworkSpeed != 0 { 260 conf.NetworkSpeed = a.config.Client.NetworkSpeed 261 } 262 if a.config.Client.CpuCompute != 0 { 263 conf.CpuCompute = a.config.Client.CpuCompute 264 } 265 if a.config.Client.MaxKillTimeout != "" { 266 dur, err := time.ParseDuration(a.config.Client.MaxKillTimeout) 267 if err != nil { 268 return nil, fmt.Errorf("Error parsing max kill timeout: %s", err) 269 } 270 conf.MaxKillTimeout = dur 271 } 272 conf.ClientMaxPort = uint(a.config.Client.ClientMaxPort) 273 conf.ClientMinPort = uint(a.config.Client.ClientMinPort) 274 275 // Setup the node 276 conf.Node = new(structs.Node) 277 conf.Node.Datacenter = a.config.Datacenter 278 conf.Node.Name = a.config.NodeName 279 conf.Node.Meta = a.config.Client.Meta 280 conf.Node.NodeClass = a.config.Client.NodeClass 281 282 // Set up the HTTP advertise address 283 conf.Node.HTTPAddr = a.config.AdvertiseAddrs.HTTP 284 285 // Reserve resources on the node. 286 r := conf.Node.Reserved 287 if r == nil { 288 r = new(structs.Resources) 289 conf.Node.Reserved = r 290 } 291 r.CPU = a.config.Client.Reserved.CPU 292 r.MemoryMB = a.config.Client.Reserved.MemoryMB 293 r.DiskMB = a.config.Client.Reserved.DiskMB 294 r.IOPS = a.config.Client.Reserved.IOPS 295 conf.GloballyReservedPorts = a.config.Client.Reserved.ParsedReservedPorts 296 297 conf.Version = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease) 298 conf.Revision = a.config.Revision 299 300 if *a.config.Consul.AutoAdvertise && a.config.Consul.ClientServiceName == "" { 301 return nil, fmt.Errorf("client_service_name must be set when auto_advertise is enabled") 302 } 303 304 conf.ConsulConfig = a.config.Consul 305 conf.VaultConfig = a.config.Vault 306 conf.StatsCollectionInterval = a.config.Telemetry.collectionInterval 307 conf.PublishNodeMetrics = a.config.Telemetry.PublishNodeMetrics 308 conf.PublishAllocationMetrics = a.config.Telemetry.PublishAllocationMetrics 309 310 // Set the TLS related configs 311 conf.TLSConfig = a.config.TLSConfig 312 conf.Node.TLSEnabled = conf.TLSConfig.EnableHTTP 313 314 // Set the GC related configs 315 conf.GCInterval = a.config.Client.GCInterval 316 conf.GCParallelDestroys = a.config.Client.GCParallelDestroys 317 conf.GCDiskUsageThreshold = a.config.Client.GCDiskUsageThreshold 318 conf.GCInodeUsageThreshold = a.config.Client.GCInodeUsageThreshold 319 conf.NoHostUUID = a.config.Client.NoHostUUID 320 321 return conf, nil 322 } 323 324 // setupServer is used to setup the server if enabled 325 func (a *Agent) setupServer() error { 326 if !a.config.Server.Enabled { 327 return nil 328 } 329 330 // Setup the configuration 331 conf, err := a.serverConfig() 332 if err != nil { 333 return fmt.Errorf("server config setup failed: %s", err) 334 } 335 336 // Sets up the keyring for gossip encryption 337 if err := a.setupKeyrings(conf); err != nil { 338 return fmt.Errorf("failed to configure keyring: %v", err) 339 } 340 341 // Create the server 342 server, err := nomad.NewServer(conf, a.consulSyncer, a.logger) 343 if err != nil { 344 return fmt.Errorf("server setup failed: %v", err) 345 } 346 a.server = server 347 348 // Consul check addresses default to bind but can be toggled to use advertise 349 httpCheckAddr := a.config.normalizedAddrs.HTTP 350 rpcCheckAddr := a.config.normalizedAddrs.RPC 351 serfCheckAddr := a.config.normalizedAddrs.Serf 352 if *a.config.Consul.ChecksUseAdvertise { 353 httpCheckAddr = a.config.AdvertiseAddrs.HTTP 354 rpcCheckAddr = a.config.AdvertiseAddrs.RPC 355 serfCheckAddr = a.config.AdvertiseAddrs.Serf 356 } 357 358 // Create the Nomad Server services for Consul 359 // TODO re-introduce HTTP/S checks when Consul 0.7.1 comes out 360 if *a.config.Consul.AutoAdvertise { 361 httpServ := &structs.Service{ 362 Name: a.config.Consul.ServerServiceName, 363 PortLabel: a.config.AdvertiseAddrs.HTTP, 364 Tags: []string{consul.ServiceTagHTTP}, 365 Checks: []*structs.ServiceCheck{ 366 &structs.ServiceCheck{ 367 Name: "Nomad Server HTTP Check", 368 Type: "http", 369 Path: "/v1/status/peers", 370 Protocol: "http", 371 Interval: serverHttpCheckInterval, 372 Timeout: serverHttpCheckTimeout, 373 PortLabel: httpCheckAddr, 374 }, 375 }, 376 } 377 rpcServ := &structs.Service{ 378 Name: a.config.Consul.ServerServiceName, 379 PortLabel: a.config.AdvertiseAddrs.RPC, 380 Tags: []string{consul.ServiceTagRPC}, 381 Checks: []*structs.ServiceCheck{ 382 &structs.ServiceCheck{ 383 Name: "Nomad Server RPC Check", 384 Type: "tcp", 385 Interval: serverRpcCheckInterval, 386 Timeout: serverRpcCheckTimeout, 387 PortLabel: rpcCheckAddr, 388 }, 389 }, 390 } 391 serfServ := &structs.Service{ 392 Name: a.config.Consul.ServerServiceName, 393 PortLabel: a.config.AdvertiseAddrs.Serf, 394 Tags: []string{consul.ServiceTagSerf}, 395 Checks: []*structs.ServiceCheck{ 396 &structs.ServiceCheck{ 397 Name: "Nomad Server Serf Check", 398 Type: "tcp", 399 Interval: serverSerfCheckInterval, 400 Timeout: serverSerfCheckTimeout, 401 PortLabel: serfCheckAddr, 402 }, 403 }, 404 } 405 406 // Add the http port check if TLS isn't enabled 407 // TODO Add TLS check when Consul 0.7.1 comes out. 408 consulServices := map[consul.ServiceKey]*structs.Service{ 409 consul.GenerateServiceKey(rpcServ): rpcServ, 410 consul.GenerateServiceKey(serfServ): serfServ, 411 } 412 if !conf.TLSConfig.EnableHTTP { 413 consulServices[consul.GenerateServiceKey(httpServ)] = httpServ 414 } 415 a.consulSyncer.SetServices(consul.ServerDomain, consulServices) 416 } 417 418 return nil 419 } 420 421 // setupKeyrings is used to initialize and load keyrings during agent startup 422 func (a *Agent) setupKeyrings(config *nomad.Config) error { 423 file := filepath.Join(a.config.DataDir, serfKeyring) 424 425 if a.config.Server.EncryptKey == "" { 426 goto LOAD 427 } 428 if _, err := os.Stat(file); err != nil { 429 if err := initKeyring(file, a.config.Server.EncryptKey); err != nil { 430 return err 431 } 432 } 433 434 LOAD: 435 if _, err := os.Stat(file); err == nil { 436 config.SerfConfig.KeyringFile = file 437 } 438 if err := loadKeyringFile(config.SerfConfig); err != nil { 439 return err 440 } 441 // Success! 442 return nil 443 } 444 445 // setupClient is used to setup the client if enabled 446 func (a *Agent) setupClient() error { 447 if !a.config.Client.Enabled { 448 return nil 449 } 450 451 // Setup the configuration 452 conf, err := a.clientConfig() 453 if err != nil { 454 return fmt.Errorf("client setup failed: %v", err) 455 } 456 457 // Reserve some ports for the plugins if we are on Windows 458 if runtime.GOOS == "windows" { 459 if err := a.reservePortsForClient(conf); err != nil { 460 return err 461 } 462 } 463 464 // Create the client 465 client, err := client.NewClient(conf, a.consulSyncer, a.logger) 466 if err != nil { 467 return fmt.Errorf("client setup failed: %v", err) 468 } 469 a.client = client 470 471 // Resolve the http check address 472 httpCheckAddr := a.config.normalizedAddrs.HTTP 473 if *a.config.Consul.ChecksUseAdvertise { 474 httpCheckAddr = a.config.AdvertiseAddrs.HTTP 475 } 476 477 // Create the Nomad Client services for Consul 478 // TODO think how we can re-introduce HTTP/S checks when Consul 0.7.1 comes 479 // out 480 if *a.config.Consul.AutoAdvertise { 481 httpServ := &structs.Service{ 482 Name: a.config.Consul.ClientServiceName, 483 PortLabel: a.config.AdvertiseAddrs.HTTP, 484 Tags: []string{consul.ServiceTagHTTP}, 485 Checks: []*structs.ServiceCheck{ 486 &structs.ServiceCheck{ 487 Name: "Nomad Client HTTP Check", 488 Type: "http", 489 Path: "/v1/agent/servers", 490 Protocol: "http", 491 Interval: clientHttpCheckInterval, 492 Timeout: clientHttpCheckTimeout, 493 PortLabel: httpCheckAddr, 494 }, 495 }, 496 } 497 if !conf.TLSConfig.EnableHTTP { 498 a.consulSyncer.SetServices(consul.ClientDomain, map[consul.ServiceKey]*structs.Service{ 499 consul.GenerateServiceKey(httpServ): httpServ, 500 }) 501 } 502 } 503 504 return nil 505 } 506 507 // reservePortsForClient reserves a range of ports for the client to use when 508 // it creates various plugins for log collection, executors, drivers, etc 509 func (a *Agent) reservePortsForClient(conf *clientconfig.Config) error { 510 // finding the device name for loopback 511 deviceName, addr, mask, err := a.findLoopbackDevice() 512 if err != nil { 513 return fmt.Errorf("error finding the device name for loopback: %v", err) 514 } 515 516 // seeing if the user has already reserved some resources on this device 517 var nr *structs.NetworkResource 518 if conf.Node.Reserved == nil { 519 conf.Node.Reserved = &structs.Resources{} 520 } 521 for _, n := range conf.Node.Reserved.Networks { 522 if n.Device == deviceName { 523 nr = n 524 } 525 } 526 // If the user hasn't already created the device, we create it 527 if nr == nil { 528 nr = &structs.NetworkResource{ 529 Device: deviceName, 530 IP: addr, 531 CIDR: mask, 532 ReservedPorts: make([]structs.Port, 0), 533 } 534 } 535 // appending the port ranges we want to use for the client to the list of 536 // reserved ports for this device 537 for i := conf.ClientMinPort; i <= conf.ClientMaxPort; i++ { 538 nr.ReservedPorts = append(nr.ReservedPorts, structs.Port{Label: fmt.Sprintf("plugin-%d", i), Value: int(i)}) 539 } 540 conf.Node.Reserved.Networks = append(conf.Node.Reserved.Networks, nr) 541 return nil 542 } 543 544 // findLoopbackDevice iterates through all the interfaces on a machine and 545 // returns the ip addr, mask of the loopback device 546 func (a *Agent) findLoopbackDevice() (string, string, string, error) { 547 var ifcs []net.Interface 548 var err error 549 ifcs, err = net.Interfaces() 550 if err != nil { 551 return "", "", "", err 552 } 553 for _, ifc := range ifcs { 554 addrs, err := ifc.Addrs() 555 if err != nil { 556 return "", "", "", err 557 } 558 for _, addr := range addrs { 559 var ip net.IP 560 switch v := addr.(type) { 561 case *net.IPNet: 562 ip = v.IP 563 case *net.IPAddr: 564 ip = v.IP 565 } 566 if ip.IsLoopback() { 567 if ip.To4() == nil { 568 continue 569 } 570 return ifc.Name, ip.String(), addr.String(), nil 571 } 572 } 573 } 574 575 return "", "", "", fmt.Errorf("no loopback devices with IPV4 addr found") 576 } 577 578 // Leave is used gracefully exit. Clients will inform servers 579 // of their departure so that allocations can be rescheduled. 580 func (a *Agent) Leave() error { 581 if a.client != nil { 582 if err := a.client.Leave(); err != nil { 583 a.logger.Printf("[ERR] agent: client leave failed: %v", err) 584 } 585 } 586 if a.server != nil { 587 if err := a.server.Leave(); err != nil { 588 a.logger.Printf("[ERR] agent: server leave failed: %v", err) 589 } 590 } 591 return nil 592 } 593 594 // Shutdown is used to terminate the agent. 595 func (a *Agent) Shutdown() error { 596 a.shutdownLock.Lock() 597 defer a.shutdownLock.Unlock() 598 599 if a.shutdown { 600 return nil 601 } 602 603 a.logger.Println("[INFO] agent: requesting shutdown") 604 if a.client != nil { 605 if err := a.client.Shutdown(); err != nil { 606 a.logger.Printf("[ERR] agent: client shutdown failed: %v", err) 607 } 608 } 609 if a.server != nil { 610 if err := a.server.Shutdown(); err != nil { 611 a.logger.Printf("[ERR] agent: server shutdown failed: %v", err) 612 } 613 } 614 615 if err := a.consulSyncer.Shutdown(); err != nil { 616 a.logger.Printf("[ERR] agent: shutting down consul service failed: %v", err) 617 } 618 619 a.logger.Println("[INFO] agent: shutdown complete") 620 a.shutdown = true 621 close(a.shutdownCh) 622 return nil 623 } 624 625 // RPC is used to make an RPC call to the Nomad servers 626 func (a *Agent) RPC(method string, args interface{}, reply interface{}) error { 627 if a.server != nil { 628 return a.server.RPC(method, args, reply) 629 } 630 return a.client.RPC(method, args, reply) 631 } 632 633 // Client returns the configured client or nil 634 func (a *Agent) Client() *client.Client { 635 return a.client 636 } 637 638 // Server returns the configured server or nil 639 func (a *Agent) Server() *nomad.Server { 640 return a.server 641 } 642 643 // Stats is used to return statistics for debugging and insight 644 // for various sub-systems 645 func (a *Agent) Stats() map[string]map[string]string { 646 stats := make(map[string]map[string]string) 647 if a.server != nil { 648 subStat := a.server.Stats() 649 for k, v := range subStat { 650 stats[k] = v 651 } 652 } 653 if a.client != nil { 654 subStat := a.client.Stats() 655 for k, v := range subStat { 656 stats[k] = v 657 } 658 } 659 return stats 660 } 661 662 // setupConsulSyncer creates the Consul tasks used by this Nomad Agent 663 // (either Client or Server mode). 664 func (a *Agent) setupConsulSyncer() error { 665 var err error 666 a.consulSyncer, err = consul.NewSyncer(a.config.Consul, a.shutdownCh, a.logger) 667 if err != nil { 668 return err 669 } 670 671 a.consulSyncer.SetAddrFinder(func(portLabel string) (string, int) { 672 host, port, err := net.SplitHostPort(portLabel) 673 if err != nil { 674 p, err := strconv.Atoi(port) 675 if err != nil { 676 return "", 0 677 } 678 return "", p 679 } 680 681 // If the addr for the service is ":port", then we fall back 682 // to Nomad's default address resolution protocol. 683 // 684 // TODO(sean@): This should poll Consul to figure out what 685 // its advertise address is and use that in order to handle 686 // the case where there is something funky like NAT on this 687 // host. For now we just use the BindAddr if set, otherwise 688 // we fall back to a loopback addr. 689 if host == "" { 690 if a.config.BindAddr != "" { 691 host = a.config.BindAddr 692 } else { 693 host = "127.0.0.1" 694 } 695 } 696 p, err := strconv.Atoi(port) 697 if err != nil { 698 return host, 0 699 } 700 return host, p 701 }) 702 703 return nil 704 }