2015-08-16 20:54:49 +00:00
|
|
|
package agent
|
|
|
|
|
2015-08-16 23:40:04 +00:00
|
|
|
import (
|
2015-08-24 00:40:27 +00:00
|
|
|
"fmt"
|
2015-08-16 23:40:04 +00:00
|
|
|
"io"
|
|
|
|
"log"
|
2015-08-31 01:10:23 +00:00
|
|
|
"net"
|
2015-08-16 23:40:04 +00:00
|
|
|
"os"
|
2015-08-31 01:10:23 +00:00
|
|
|
"path/filepath"
|
2016-03-10 07:25:31 +00:00
|
|
|
"runtime"
|
2016-05-11 22:24:37 +00:00
|
|
|
"strconv"
|
2015-08-16 23:40:04 +00:00
|
|
|
"sync"
|
2015-10-29 13:47:06 +00:00
|
|
|
"time"
|
2015-08-16 21:34:38 +00:00
|
|
|
|
2015-08-23 23:53:15 +00:00
|
|
|
"github.com/hashicorp/nomad/client"
|
2016-02-12 21:21:56 +00:00
|
|
|
clientconfig "github.com/hashicorp/nomad/client/config"
|
2016-06-08 06:02:37 +00:00
|
|
|
"github.com/hashicorp/nomad/command/agent/consul"
|
2015-08-16 23:40:04 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad"
|
2015-08-31 01:10:23 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
2015-08-16 23:40:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Agent is a long running daemon that is used to run both
|
|
|
|
// clients and servers. Servers are responsible for managing
|
|
|
|
// state and making scheduling decisions. Clients can be
|
|
|
|
// scheduled to, and are responsible for interfacing with
|
|
|
|
// servers to run allocations.
|
2015-08-16 20:54:49 +00:00
|
|
|
type Agent struct {
|
2015-08-16 23:40:04 +00:00
|
|
|
config *Config
|
|
|
|
logger *log.Logger
|
|
|
|
logOutput io.Writer
|
|
|
|
|
2016-05-23 18:09:31 +00:00
|
|
|
// consulSyncer registers the Nomad agent with the Consul Agent
|
|
|
|
consulSyncer *consul.Syncer
|
|
|
|
|
2016-05-27 08:35:10 +00:00
|
|
|
client *client.Client
|
2016-06-11 03:26:15 +00:00
|
|
|
clientHTTPAddr string
|
|
|
|
clientRPCAddr string
|
2016-05-11 22:24:37 +00:00
|
|
|
|
2016-05-27 08:35:10 +00:00
|
|
|
server *nomad.Server
|
2016-06-11 03:26:15 +00:00
|
|
|
serverHTTPAddr string
|
|
|
|
serverRPCAddr string
|
2016-05-27 22:58:28 +00:00
|
|
|
serverSerfAddr string
|
2015-08-16 23:40:04 +00:00
|
|
|
|
|
|
|
shutdown bool
|
2016-05-27 10:47:49 +00:00
|
|
|
shutdownCh chan struct{}
|
2015-08-16 23:40:04 +00:00
|
|
|
shutdownLock sync.Mutex
|
2015-08-16 20:54:49 +00:00
|
|
|
}
|
|
|
|
|
2015-08-16 23:40:04 +00:00
|
|
|
// NewAgent is used to create a new agent with the given configuration
|
2015-08-16 21:34:38 +00:00
|
|
|
func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) {
|
2015-08-16 23:40:04 +00:00
|
|
|
// Ensure we have a log sink
|
|
|
|
if logOutput == nil {
|
|
|
|
logOutput = os.Stderr
|
|
|
|
}
|
|
|
|
|
2016-05-27 10:47:49 +00:00
|
|
|
shutdownCh := make(chan struct{})
|
2015-08-16 23:40:04 +00:00
|
|
|
a := &Agent{
|
|
|
|
config: config,
|
|
|
|
logger: log.New(logOutput, "", log.LstdFlags),
|
|
|
|
logOutput: logOutput,
|
2016-05-24 01:12:58 +00:00
|
|
|
shutdownCh: shutdownCh,
|
2015-08-16 23:40:04 +00:00
|
|
|
}
|
2015-08-24 00:40:27 +00:00
|
|
|
|
2016-05-23 18:09:31 +00:00
|
|
|
if err := a.setupConsulSyncer(shutdownCh); err != nil {
|
2016-06-08 06:31:19 +00:00
|
|
|
return nil, fmt.Errorf("Failed to initialize Consul syncer task: %v", err)
|
2016-05-23 18:09:31 +00:00
|
|
|
}
|
2015-08-24 00:40:27 +00:00
|
|
|
if err := a.setupServer(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := a.setupClient(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if a.client == nil && a.server == nil {
|
|
|
|
return nil, fmt.Errorf("must have at least client or server mode enabled")
|
|
|
|
}
|
2016-06-08 06:31:19 +00:00
|
|
|
|
2016-06-13 05:30:52 +00:00
|
|
|
// The Nomad Agent runs the consul.Syncer regardless of whether or not the
|
|
|
|
// Agent is running in Client or Server mode (or both), and regardless of
|
|
|
|
// the consul.auto_register parameter. The Client and Server both reuse the
|
|
|
|
// same consul.Syncer instance. This Syncer task periodically executes
|
|
|
|
// callbacks that update Consul. The reason the Syncer is always running is
|
|
|
|
// because one of the callbacks is attempts to self-bootstrap Nomad using
|
|
|
|
// information found in Consul.
|
2016-05-24 04:45:13 +00:00
|
|
|
go a.consulSyncer.Run()
|
2016-05-22 15:24:54 +00:00
|
|
|
|
2015-08-16 21:34:38 +00:00
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2015-09-11 19:02:22 +00:00
|
|
|
// serverConfig is used to generate a new server configuration struct
|
|
|
|
// for initializing a nomad server.
|
|
|
|
func (a *Agent) serverConfig() (*nomad.Config, error) {
|
2015-09-06 01:41:00 +00:00
|
|
|
conf := a.config.NomadConfig
|
|
|
|
if conf == nil {
|
|
|
|
conf = nomad.DefaultConfig()
|
|
|
|
}
|
2015-08-24 00:40:27 +00:00
|
|
|
conf.LogOutput = a.logOutput
|
|
|
|
conf.DevMode = a.config.DevMode
|
2015-08-31 01:10:23 +00:00
|
|
|
conf.Build = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease)
|
2016-03-19 04:44:22 +00:00
|
|
|
if a.config.Region != "" {
|
2015-08-31 01:14:40 +00:00
|
|
|
conf.Region = a.config.Region
|
|
|
|
}
|
|
|
|
if a.config.Datacenter != "" {
|
|
|
|
conf.Datacenter = a.config.Datacenter
|
|
|
|
}
|
|
|
|
if a.config.NodeName != "" {
|
|
|
|
conf.NodeName = a.config.NodeName
|
|
|
|
}
|
2015-08-31 01:10:23 +00:00
|
|
|
if a.config.Server.BootstrapExpect > 0 {
|
2015-09-22 21:25:43 +00:00
|
|
|
if a.config.Server.BootstrapExpect == 1 {
|
|
|
|
conf.Bootstrap = true
|
|
|
|
} else {
|
|
|
|
conf.BootstrapExpect = a.config.Server.BootstrapExpect
|
|
|
|
}
|
2015-08-31 01:10:23 +00:00
|
|
|
}
|
|
|
|
if a.config.DataDir != "" {
|
|
|
|
conf.DataDir = filepath.Join(a.config.DataDir, "server")
|
|
|
|
}
|
|
|
|
if a.config.Server.DataDir != "" {
|
|
|
|
conf.DataDir = a.config.Server.DataDir
|
|
|
|
}
|
|
|
|
if a.config.Server.ProtocolVersion != 0 {
|
|
|
|
conf.ProtocolVersion = uint8(a.config.Server.ProtocolVersion)
|
|
|
|
}
|
|
|
|
if a.config.Server.NumSchedulers != 0 {
|
|
|
|
conf.NumSchedulers = a.config.Server.NumSchedulers
|
|
|
|
}
|
|
|
|
if len(a.config.Server.EnabledSchedulers) != 0 {
|
|
|
|
conf.EnabledSchedulers = a.config.Server.EnabledSchedulers
|
|
|
|
}
|
2015-09-11 01:37:42 +00:00
|
|
|
|
|
|
|
// Set up the advertise addrs
|
|
|
|
if addr := a.config.AdvertiseAddrs.Serf; addr != "" {
|
2015-09-11 18:27:14 +00:00
|
|
|
serfAddr, err := net.ResolveTCPAddr("tcp", addr)
|
|
|
|
if err != nil {
|
2015-09-11 19:02:22 +00:00
|
|
|
return nil, fmt.Errorf("error resolving serf advertise address: %s", err)
|
2015-09-11 18:27:14 +00:00
|
|
|
}
|
|
|
|
conf.SerfConfig.MemberlistConfig.AdvertiseAddr = serfAddr.IP.String()
|
|
|
|
conf.SerfConfig.MemberlistConfig.AdvertisePort = serfAddr.Port
|
2015-09-11 01:37:42 +00:00
|
|
|
}
|
|
|
|
if addr := a.config.AdvertiseAddrs.RPC; addr != "" {
|
2015-09-11 18:27:14 +00:00
|
|
|
rpcAddr, err := net.ResolveTCPAddr("tcp", addr)
|
|
|
|
if err != nil {
|
2015-09-11 19:02:22 +00:00
|
|
|
return nil, fmt.Errorf("error resolving rpc advertise address: %s", err)
|
2015-08-31 01:10:23 +00:00
|
|
|
}
|
2015-09-11 18:27:14 +00:00
|
|
|
conf.RPCAdvertise = rpcAddr
|
2015-08-31 01:10:23 +00:00
|
|
|
}
|
2015-09-11 01:37:42 +00:00
|
|
|
|
|
|
|
// Set up the bind addresses
|
2015-09-11 00:48:02 +00:00
|
|
|
if addr := a.config.BindAddr; addr != "" {
|
2015-09-11 01:37:42 +00:00
|
|
|
conf.RPCAddr.IP = net.ParseIP(addr)
|
2015-09-11 00:48:02 +00:00
|
|
|
conf.SerfConfig.MemberlistConfig.BindAddr = addr
|
2015-08-31 01:10:23 +00:00
|
|
|
}
|
2015-09-11 01:37:42 +00:00
|
|
|
if addr := a.config.Addresses.RPC; addr != "" {
|
|
|
|
conf.RPCAddr.IP = net.ParseIP(addr)
|
|
|
|
}
|
2016-04-07 20:25:38 +00:00
|
|
|
|
2015-09-11 01:37:42 +00:00
|
|
|
if addr := a.config.Addresses.Serf; addr != "" {
|
|
|
|
conf.SerfConfig.MemberlistConfig.BindAddr = addr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up the ports
|
|
|
|
if port := a.config.Ports.RPC; port != 0 {
|
|
|
|
conf.RPCAddr.Port = port
|
|
|
|
}
|
|
|
|
if port := a.config.Ports.Serf; port != 0 {
|
|
|
|
conf.SerfConfig.MemberlistConfig.BindPort = port
|
|
|
|
}
|
2016-05-25 00:27:09 +00:00
|
|
|
|
2016-05-27 08:35:10 +00:00
|
|
|
// Resolve the Server's HTTP Address
|
|
|
|
if a.config.AdvertiseAddrs.HTTP != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverHTTPAddr = a.config.AdvertiseAddrs.HTTP
|
2016-05-27 08:35:10 +00:00
|
|
|
} else if a.config.Addresses.HTTP != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverHTTPAddr = fmt.Sprintf("%v:%v", a.config.Addresses.HTTP, a.config.Ports.HTTP)
|
2016-05-27 08:35:10 +00:00
|
|
|
} else if a.config.BindAddr != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverHTTPAddr = fmt.Sprintf("%v:%v", a.config.BindAddr, a.config.Ports.HTTP)
|
2016-05-27 08:35:10 +00:00
|
|
|
} else {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverHTTPAddr = fmt.Sprintf("%v:%v", "127.0.0.1", a.config.Ports.HTTP)
|
2016-05-27 08:35:10 +00:00
|
|
|
}
|
2016-06-11 03:26:15 +00:00
|
|
|
addr, err := net.ResolveTCPAddr("tcp", a.serverHTTPAddr)
|
2016-05-27 08:35:10 +00:00
|
|
|
if err != nil {
|
2016-06-11 22:17:20 +00:00
|
|
|
return nil, fmt.Errorf("error resolving HTTP addr %+q: %v", a.serverHTTPAddr, err)
|
2016-05-27 08:35:10 +00:00
|
|
|
}
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverHTTPAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
|
2016-05-27 08:35:10 +00:00
|
|
|
|
|
|
|
// Resolve the Server's RPC Address
|
2016-05-27 07:42:31 +00:00
|
|
|
if a.config.AdvertiseAddrs.RPC != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverRPCAddr = a.config.AdvertiseAddrs.RPC
|
2016-05-27 07:42:31 +00:00
|
|
|
} else if a.config.Addresses.RPC != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverRPCAddr = fmt.Sprintf("%v:%v", a.config.Addresses.RPC, a.config.Ports.RPC)
|
2016-05-25 00:27:09 +00:00
|
|
|
} else if a.config.BindAddr != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverRPCAddr = fmt.Sprintf("%v:%v", a.config.BindAddr, a.config.Ports.RPC)
|
2016-05-25 01:33:24 +00:00
|
|
|
} else {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverRPCAddr = fmt.Sprintf("%v:%v", "127.0.0.1", a.config.Ports.RPC)
|
2016-05-14 08:09:05 +00:00
|
|
|
}
|
2016-06-11 03:26:15 +00:00
|
|
|
addr, err = net.ResolveTCPAddr("tcp", a.serverRPCAddr)
|
2016-05-27 07:42:31 +00:00
|
|
|
if err != nil {
|
2016-06-11 22:17:20 +00:00
|
|
|
return nil, fmt.Errorf("error resolving RPC addr %+q: %v", a.serverRPCAddr, err)
|
2016-05-27 07:42:31 +00:00
|
|
|
}
|
2016-06-11 03:26:15 +00:00
|
|
|
a.serverRPCAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
|
2015-08-24 00:40:27 +00:00
|
|
|
|
2016-06-01 08:08:15 +00:00
|
|
|
// Resolve the Server's Serf Address
|
|
|
|
if a.config.AdvertiseAddrs.Serf != "" {
|
|
|
|
a.serverSerfAddr = a.config.AdvertiseAddrs.Serf
|
|
|
|
} else if a.config.Addresses.Serf != "" {
|
|
|
|
a.serverSerfAddr = fmt.Sprintf("%v:%v", a.config.Addresses.Serf, a.config.Ports.Serf)
|
|
|
|
} else if a.config.BindAddr != "" {
|
|
|
|
a.serverSerfAddr = fmt.Sprintf("%v:%v", a.config.BindAddr, a.config.Ports.Serf)
|
|
|
|
} else {
|
|
|
|
a.serverSerfAddr = fmt.Sprintf("%v:%v", "127.0.0.1", a.config.Ports.Serf)
|
|
|
|
}
|
|
|
|
addr, err = net.ResolveTCPAddr("tcp", a.serverSerfAddr)
|
|
|
|
if err != nil {
|
2016-06-11 22:17:20 +00:00
|
|
|
return nil, fmt.Errorf("error resolving Serf addr %+q: %v", a.serverSerfAddr, err)
|
2016-06-01 08:08:15 +00:00
|
|
|
}
|
|
|
|
a.serverSerfAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
|
|
|
|
|
2015-10-29 13:47:06 +00:00
|
|
|
if gcThreshold := a.config.Server.NodeGCThreshold; gcThreshold != "" {
|
|
|
|
dur, err := time.ParseDuration(gcThreshold)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
conf.NodeGCThreshold = dur
|
|
|
|
}
|
|
|
|
|
2016-03-04 23:44:12 +00:00
|
|
|
if heartbeatGrace := a.config.Server.HeartbeatGrace; heartbeatGrace != "" {
|
|
|
|
dur, err := time.ParseDuration(heartbeatGrace)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
conf.HeartbeatGrace = dur
|
|
|
|
}
|
|
|
|
|
2015-09-11 19:02:22 +00:00
|
|
|
return conf, nil
|
|
|
|
}
|
|
|
|
|
2016-02-16 21:42:48 +00:00
|
|
|
// clientConfig is used to generate a new client configuration struct
|
2016-05-22 15:24:54 +00:00
|
|
|
// for initializing a Nomad client.
|
2016-02-16 21:42:48 +00:00
|
|
|
func (a *Agent) clientConfig() (*clientconfig.Config, error) {
|
2015-08-24 00:40:27 +00:00
|
|
|
// Setup the configuration
|
2015-09-06 01:41:00 +00:00
|
|
|
conf := a.config.ClientConfig
|
|
|
|
if conf == nil {
|
2016-05-31 18:58:02 +00:00
|
|
|
conf = clientconfig.DefaultConfig()
|
2015-09-06 01:41:00 +00:00
|
|
|
}
|
2015-08-24 00:40:27 +00:00
|
|
|
if a.server != nil {
|
|
|
|
conf.RPCHandler = a.server
|
|
|
|
}
|
|
|
|
conf.LogOutput = a.logOutput
|
|
|
|
conf.DevMode = a.config.DevMode
|
2015-08-31 01:14:40 +00:00
|
|
|
if a.config.Region != "" {
|
|
|
|
conf.Region = a.config.Region
|
|
|
|
}
|
|
|
|
if a.config.DataDir != "" {
|
|
|
|
conf.StateDir = filepath.Join(a.config.DataDir, "client")
|
|
|
|
conf.AllocDir = filepath.Join(a.config.DataDir, "alloc")
|
|
|
|
}
|
|
|
|
if a.config.Client.StateDir != "" {
|
|
|
|
conf.StateDir = a.config.Client.StateDir
|
|
|
|
}
|
|
|
|
if a.config.Client.AllocDir != "" {
|
|
|
|
conf.AllocDir = a.config.Client.AllocDir
|
|
|
|
}
|
2015-08-31 01:10:23 +00:00
|
|
|
conf.Servers = a.config.Client.Servers
|
2015-10-02 07:29:18 +00:00
|
|
|
if a.config.Client.NetworkInterface != "" {
|
|
|
|
conf.NetworkInterface = a.config.Client.NetworkInterface
|
2015-10-01 15:31:47 +00:00
|
|
|
}
|
2015-09-28 23:45:32 +00:00
|
|
|
conf.Options = a.config.Client.Options
|
2015-10-03 00:32:11 +00:00
|
|
|
if a.config.Client.NetworkSpeed != 0 {
|
|
|
|
conf.NetworkSpeed = a.config.Client.NetworkSpeed
|
|
|
|
}
|
2015-12-23 00:10:30 +00:00
|
|
|
if a.config.Client.MaxKillTimeout != "" {
|
|
|
|
dur, err := time.ParseDuration(a.config.Client.MaxKillTimeout)
|
|
|
|
if err != nil {
|
2016-02-16 21:42:48 +00:00
|
|
|
return nil, fmt.Errorf("Error parsing retry interval: %s", err)
|
2015-12-23 00:10:30 +00:00
|
|
|
}
|
|
|
|
conf.MaxKillTimeout = dur
|
|
|
|
}
|
2016-03-09 20:25:30 +00:00
|
|
|
conf.ClientMaxPort = uint(a.config.Client.ClientMaxPort)
|
|
|
|
conf.ClientMinPort = uint(a.config.Client.ClientMinPort)
|
2015-08-31 01:10:23 +00:00
|
|
|
|
|
|
|
// Setup the node
|
|
|
|
conf.Node = new(structs.Node)
|
|
|
|
conf.Node.Datacenter = a.config.Datacenter
|
|
|
|
conf.Node.Name = a.config.NodeName
|
|
|
|
conf.Node.Meta = a.config.Client.Meta
|
|
|
|
conf.Node.NodeClass = a.config.Client.NodeClass
|
2016-03-19 07:54:19 +00:00
|
|
|
|
2016-05-27 08:35:10 +00:00
|
|
|
// Resolve the Client's HTTP address
|
|
|
|
if a.config.AdvertiseAddrs.HTTP != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientHTTPAddr = a.config.AdvertiseAddrs.HTTP
|
2016-05-27 08:35:10 +00:00
|
|
|
} else if a.config.Addresses.HTTP != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientHTTPAddr = fmt.Sprintf("%v:%v", a.config.Addresses.HTTP, a.config.Ports.HTTP)
|
2016-05-27 08:35:10 +00:00
|
|
|
} else if a.config.BindAddr != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientHTTPAddr = fmt.Sprintf("%v:%v", a.config.BindAddr, a.config.Ports.HTTP)
|
2016-05-27 08:35:10 +00:00
|
|
|
} else {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientHTTPAddr = fmt.Sprintf("%v:%v", "127.0.0.1", a.config.Ports.HTTP)
|
2016-01-27 01:08:18 +00:00
|
|
|
}
|
2016-06-11 03:26:15 +00:00
|
|
|
addr, err := net.ResolveTCPAddr("tcp", a.clientHTTPAddr)
|
2016-05-27 08:35:10 +00:00
|
|
|
if err != nil {
|
2016-06-11 22:17:20 +00:00
|
|
|
return nil, fmt.Errorf("error resolving HTTP addr %+q: %v", a.clientHTTPAddr, err)
|
2016-05-27 08:35:10 +00:00
|
|
|
}
|
|
|
|
httpAddr := fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
|
2016-01-27 21:34:01 +00:00
|
|
|
conf.Node.HTTPAddr = httpAddr
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientHTTPAddr = httpAddr
|
2016-03-14 02:05:41 +00:00
|
|
|
|
2016-05-27 08:35:10 +00:00
|
|
|
// Resolve the Client's RPC address
|
|
|
|
if a.config.AdvertiseAddrs.RPC != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientRPCAddr = a.config.AdvertiseAddrs.RPC
|
2016-05-27 08:35:10 +00:00
|
|
|
} else if a.config.Addresses.RPC != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientRPCAddr = fmt.Sprintf("%v:%v", a.config.Addresses.RPC, a.config.Ports.RPC)
|
2016-05-27 08:35:10 +00:00
|
|
|
} else if a.config.BindAddr != "" {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientRPCAddr = fmt.Sprintf("%v:%v", a.config.BindAddr, a.config.Ports.RPC)
|
2016-05-27 08:35:10 +00:00
|
|
|
} else {
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientRPCAddr = fmt.Sprintf("%v:%v", "127.0.0.1", a.config.Ports.RPC)
|
2016-05-27 08:35:10 +00:00
|
|
|
}
|
2016-06-11 03:26:15 +00:00
|
|
|
addr, err = net.ResolveTCPAddr("tcp", a.clientRPCAddr)
|
2016-05-27 08:35:10 +00:00
|
|
|
if err != nil {
|
2016-06-11 22:17:20 +00:00
|
|
|
return nil, fmt.Errorf("error resolving RPC addr %+q: %v", a.clientRPCAddr, err)
|
2016-05-27 08:35:10 +00:00
|
|
|
}
|
2016-06-11 03:26:15 +00:00
|
|
|
a.clientRPCAddr = fmt.Sprintf("%s:%d", addr.IP.String(), addr.Port)
|
2016-05-27 08:35:10 +00:00
|
|
|
|
2016-03-14 02:05:41 +00:00
|
|
|
// Reserve resources on the node.
|
|
|
|
r := conf.Node.Reserved
|
|
|
|
if r == nil {
|
|
|
|
r = new(structs.Resources)
|
|
|
|
conf.Node.Reserved = r
|
|
|
|
}
|
|
|
|
r.CPU = a.config.Client.Reserved.CPU
|
|
|
|
r.MemoryMB = a.config.Client.Reserved.MemoryMB
|
|
|
|
r.DiskMB = a.config.Client.Reserved.DiskMB
|
|
|
|
r.IOPS = a.config.Client.Reserved.IOPS
|
|
|
|
conf.GloballyReservedPorts = a.config.Client.Reserved.ParsedReservedPorts
|
|
|
|
|
2016-03-23 00:12:30 +00:00
|
|
|
conf.Version = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease)
|
|
|
|
conf.Revision = a.config.Revision
|
2016-03-19 07:54:19 +00:00
|
|
|
|
2016-05-27 10:49:29 +00:00
|
|
|
conf.ConsulConfig = a.config.Consul
|
2016-05-11 22:24:37 +00:00
|
|
|
|
2016-05-25 05:30:10 +00:00
|
|
|
conf.StatsCollectionInterval = a.config.Client.StatsConfig.collectionInterval
|
|
|
|
|
2016-02-16 21:42:48 +00:00
|
|
|
return conf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// setupServer is used to setup the server if enabled
|
|
|
|
func (a *Agent) setupServer() error {
|
|
|
|
if !a.config.Server.Enabled {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the configuration
|
|
|
|
conf, err := a.serverConfig()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("server config setup failed: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the server
|
|
|
|
server, err := nomad.NewServer(conf)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("server setup failed: %v", err)
|
|
|
|
}
|
|
|
|
a.server = server
|
2016-06-10 01:12:02 +00:00
|
|
|
|
|
|
|
// Create the Nomad Server services for Consul
|
2016-06-10 05:05:55 +00:00
|
|
|
if a.config.Consul.AutoRegister && a.config.Consul.ServerServiceName != "" {
|
2016-06-13 23:29:07 +00:00
|
|
|
httpServ := &structs.Service{
|
|
|
|
Name: a.config.Consul.ServerServiceName,
|
|
|
|
PortLabel: a.serverHTTPAddr,
|
|
|
|
Tags: []string{consul.ServiceTagHTTP},
|
|
|
|
}
|
|
|
|
rpcServ := &structs.Service{
|
|
|
|
Name: a.config.Consul.ServerServiceName,
|
|
|
|
PortLabel: a.serverRPCAddr,
|
|
|
|
Tags: []string{consul.ServiceTagRPC},
|
|
|
|
}
|
|
|
|
serfServ := &structs.Service{
|
|
|
|
PortLabel: a.serverSerfAddr,
|
|
|
|
Name: a.config.Consul.ServerServiceName,
|
|
|
|
Tags: []string{consul.ServiceTagSerf},
|
|
|
|
}
|
|
|
|
a.consulSyncer.SetServices(consul.ServerDomain, map[consul.ServiceKey]*structs.Service{
|
|
|
|
consul.GenerateServiceKey(httpServ): httpServ,
|
|
|
|
consul.GenerateServiceKey(rpcServ): rpcServ,
|
|
|
|
consul.GenerateServiceKey(serfServ): serfServ,
|
|
|
|
})
|
2016-06-10 01:12:02 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 21:42:48 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// setupClient is used to setup the client if enabled
|
|
|
|
func (a *Agent) setupClient() error {
|
|
|
|
if !a.config.Client.Enabled {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the configuration
|
|
|
|
conf, err := a.clientConfig()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("client setup failed: %v", err)
|
|
|
|
}
|
2015-08-31 01:10:23 +00:00
|
|
|
|
2016-03-10 07:25:31 +00:00
|
|
|
// Reserve some ports for the plugins if we are on Windows
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
if err := a.reservePortsForClient(conf); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-02-05 23:17:15 +00:00
|
|
|
}
|
|
|
|
|
2015-08-31 01:10:23 +00:00
|
|
|
// Create the client
|
2016-05-27 09:19:01 +00:00
|
|
|
client, err := client.NewClient(conf, a.consulSyncer)
|
2015-08-24 00:40:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("client setup failed: %v", err)
|
|
|
|
}
|
|
|
|
a.client = client
|
2016-06-10 01:12:02 +00:00
|
|
|
|
|
|
|
// Create the Nomad Server services for Consul
|
2016-06-10 05:05:55 +00:00
|
|
|
if a.config.Consul.AutoRegister && a.config.Consul.ClientServiceName != "" {
|
2016-06-13 23:29:07 +00:00
|
|
|
httpServ := &structs.Service{
|
|
|
|
Name: a.config.Consul.ClientServiceName,
|
|
|
|
PortLabel: a.clientHTTPAddr,
|
|
|
|
Tags: []string{consul.ServiceTagHTTP},
|
|
|
|
}
|
|
|
|
rpcServ := &structs.Service{
|
|
|
|
Name: a.config.Consul.ClientServiceName,
|
|
|
|
PortLabel: a.clientRPCAddr,
|
|
|
|
Tags: []string{consul.ServiceTagRPC},
|
|
|
|
}
|
|
|
|
a.consulSyncer.SetServices(consul.ClientDomain, map[consul.ServiceKey]*structs.Service{
|
|
|
|
consul.GenerateServiceKey(httpServ): httpServ,
|
|
|
|
consul.GenerateServiceKey(rpcServ): rpcServ,
|
|
|
|
})
|
2016-06-10 01:12:02 +00:00
|
|
|
}
|
|
|
|
|
2015-08-24 00:40:27 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-01 10:08:39 +00:00
|
|
|
// reservePortsForClient reserves a range of ports for the client to use when
|
2016-02-12 22:25:32 +00:00
|
|
|
// it creates various plugins for log collection, executors, drivers, etc
|
2016-02-12 21:21:56 +00:00
|
|
|
func (a *Agent) reservePortsForClient(conf *clientconfig.Config) error {
|
2016-02-12 22:25:32 +00:00
|
|
|
// finding the device name for loopback
|
2016-02-12 21:21:56 +00:00
|
|
|
deviceName, addr, mask, err := a.findLoopbackDevice()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error finding the device name for loopback: %v", err)
|
|
|
|
}
|
2016-02-12 22:25:32 +00:00
|
|
|
|
|
|
|
// seeing if the user has already reserved some resources on this device
|
2016-02-12 21:21:56 +00:00
|
|
|
var nr *structs.NetworkResource
|
|
|
|
if conf.Node.Reserved == nil {
|
|
|
|
conf.Node.Reserved = &structs.Resources{}
|
|
|
|
}
|
|
|
|
for _, n := range conf.Node.Reserved.Networks {
|
|
|
|
if n.Device == deviceName {
|
|
|
|
nr = n
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 22:25:32 +00:00
|
|
|
// If the user hasn't already created the device, we create it
|
2016-02-12 21:21:56 +00:00
|
|
|
if nr == nil {
|
|
|
|
nr = &structs.NetworkResource{
|
|
|
|
Device: deviceName,
|
|
|
|
IP: addr,
|
|
|
|
CIDR: mask,
|
|
|
|
ReservedPorts: make([]structs.Port, 0),
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 22:25:32 +00:00
|
|
|
// appending the port ranges we want to use for the client to the list of
|
|
|
|
// reserved ports for this device
|
2016-02-12 21:21:56 +00:00
|
|
|
for i := conf.ClientMinPort; i <= conf.ClientMaxPort; i++ {
|
|
|
|
nr.ReservedPorts = append(nr.ReservedPorts, structs.Port{Label: fmt.Sprintf("plugin-%d", i), Value: int(i)})
|
|
|
|
}
|
|
|
|
conf.Node.Reserved.Networks = append(conf.Node.Reserved.Networks, nr)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-02-12 22:25:32 +00:00
|
|
|
// findLoopbackDevice iterates through all the interfaces on a machine and
|
|
|
|
// returns the ip addr, mask of the loopback device
|
2016-02-12 21:21:56 +00:00
|
|
|
func (a *Agent) findLoopbackDevice() (string, string, string, error) {
|
2016-02-05 23:17:15 +00:00
|
|
|
var ifcs []net.Interface
|
|
|
|
var err error
|
|
|
|
ifcs, err = net.Interfaces()
|
|
|
|
if err != nil {
|
2016-02-12 21:21:56 +00:00
|
|
|
return "", "", "", err
|
2016-02-05 23:17:15 +00:00
|
|
|
}
|
|
|
|
for _, ifc := range ifcs {
|
|
|
|
addrs, err := ifc.Addrs()
|
|
|
|
if err != nil {
|
2016-02-12 22:25:32 +00:00
|
|
|
return "", "", "", err
|
2016-02-05 23:17:15 +00:00
|
|
|
}
|
|
|
|
for _, addr := range addrs {
|
2016-02-12 21:21:56 +00:00
|
|
|
var ip net.IP
|
|
|
|
switch v := addr.(type) {
|
|
|
|
case *net.IPNet:
|
|
|
|
ip = v.IP
|
|
|
|
case *net.IPAddr:
|
|
|
|
ip = v.IP
|
|
|
|
}
|
|
|
|
if ip.IsLoopback() {
|
2016-02-12 22:25:32 +00:00
|
|
|
if ip.To4() == nil {
|
|
|
|
continue
|
|
|
|
}
|
2016-02-12 21:21:56 +00:00
|
|
|
return ifc.Name, ip.String(), addr.String(), nil
|
2016-02-05 23:17:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 21:21:56 +00:00
|
|
|
|
2016-02-12 22:25:32 +00:00
|
|
|
return "", "", "", fmt.Errorf("no loopback devices with IPV4 addr found")
|
2016-02-05 23:17:15 +00:00
|
|
|
}
|
|
|
|
|
2015-08-16 23:40:04 +00:00
|
|
|
// Leave is used gracefully exit. Clients will inform servers
|
|
|
|
// of their departure so that allocations can be rescheduled.
|
2015-08-16 20:54:49 +00:00
|
|
|
func (a *Agent) Leave() error {
|
2015-08-23 23:53:15 +00:00
|
|
|
if a.client != nil {
|
|
|
|
if err := a.client.Leave(); err != nil {
|
|
|
|
a.logger.Printf("[ERR] agent: client leave failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if a.server != nil {
|
|
|
|
if err := a.server.Leave(); err != nil {
|
|
|
|
a.logger.Printf("[ERR] agent: server leave failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2015-08-16 20:54:49 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-08-16 23:40:04 +00:00
|
|
|
// Shutdown is used to terminate the agent.
|
2015-08-16 20:54:49 +00:00
|
|
|
func (a *Agent) Shutdown() error {
|
2015-08-16 23:40:04 +00:00
|
|
|
a.shutdownLock.Lock()
|
|
|
|
defer a.shutdownLock.Unlock()
|
|
|
|
|
|
|
|
if a.shutdown {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
a.logger.Println("[INFO] agent: requesting shutdown")
|
2015-08-23 23:53:15 +00:00
|
|
|
if a.client != nil {
|
|
|
|
if err := a.client.Shutdown(); err != nil {
|
|
|
|
a.logger.Printf("[ERR] agent: client shutdown failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2015-08-16 23:40:04 +00:00
|
|
|
if a.server != nil {
|
2015-08-23 23:53:15 +00:00
|
|
|
if err := a.server.Shutdown(); err != nil {
|
|
|
|
a.logger.Printf("[ERR] agent: server shutdown failed: %v", err)
|
|
|
|
}
|
2015-08-16 23:40:04 +00:00
|
|
|
}
|
|
|
|
|
2016-06-10 03:35:07 +00:00
|
|
|
if err := a.consulSyncer.Shutdown(); err != nil {
|
|
|
|
a.logger.Printf("[ERR] agent: shutting down consul service failed: %v", err)
|
2016-05-11 22:24:37 +00:00
|
|
|
}
|
|
|
|
|
2015-08-16 23:40:04 +00:00
|
|
|
a.logger.Println("[INFO] agent: shutdown complete")
|
|
|
|
a.shutdown = true
|
|
|
|
close(a.shutdownCh)
|
2015-08-23 23:53:15 +00:00
|
|
|
return nil
|
2015-08-16 23:40:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// RPC is used to make an RPC call to the Nomad servers
|
|
|
|
func (a *Agent) RPC(method string, args interface{}, reply interface{}) error {
|
|
|
|
if a.server != nil {
|
|
|
|
return a.server.RPC(method, args, reply)
|
|
|
|
}
|
|
|
|
return a.client.RPC(method, args, reply)
|
2015-08-16 20:54:49 +00:00
|
|
|
}
|
2015-08-24 00:40:27 +00:00
|
|
|
|
|
|
|
// Client returns the configured client or nil
|
|
|
|
func (a *Agent) Client() *client.Client {
|
|
|
|
return a.client
|
|
|
|
}
|
|
|
|
|
|
|
|
// Server returns the configured server or nil
|
|
|
|
func (a *Agent) Server() *nomad.Server {
|
|
|
|
return a.server
|
|
|
|
}
|
2015-08-31 01:20:00 +00:00
|
|
|
|
|
|
|
// Stats is used to return statistics for debugging and insight
|
|
|
|
// for various sub-systems
|
|
|
|
func (a *Agent) Stats() map[string]map[string]string {
|
|
|
|
stats := make(map[string]map[string]string)
|
|
|
|
if a.server != nil {
|
|
|
|
subStat := a.server.Stats()
|
|
|
|
for k, v := range subStat {
|
|
|
|
stats[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if a.client != nil {
|
|
|
|
subStat := a.client.Stats()
|
|
|
|
for k, v := range subStat {
|
|
|
|
stats[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stats
|
|
|
|
}
|
2016-05-11 22:24:37 +00:00
|
|
|
|
2016-06-10 01:12:02 +00:00
|
|
|
// setupConsulSyncer creates the Consul tasks used by this Nomad Agent
|
2016-06-08 06:31:19 +00:00
|
|
|
// (either Client or Server mode).
|
|
|
|
func (a *Agent) setupConsulSyncer(shutdownCh chan struct{}) error {
|
|
|
|
var err error
|
2016-06-02 16:15:30 +00:00
|
|
|
a.consulSyncer, err = consul.NewSyncer(a.config.Consul, shutdownCh, a.logger)
|
2016-06-08 06:31:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-05-11 22:24:37 +00:00
|
|
|
|
2016-05-24 04:43:09 +00:00
|
|
|
a.consulSyncer.SetAddrFinder(func(portLabel string) (string, int) {
|
2016-05-14 07:36:26 +00:00
|
|
|
host, port, err := net.SplitHostPort(portLabel)
|
2016-05-11 22:24:37 +00:00
|
|
|
if err != nil {
|
2016-06-07 14:55:35 +00:00
|
|
|
p, err := strconv.Atoi(port)
|
|
|
|
if err != nil {
|
|
|
|
return "", 0
|
|
|
|
}
|
|
|
|
return "", p
|
2016-05-11 22:24:37 +00:00
|
|
|
}
|
2016-05-15 00:08:19 +00:00
|
|
|
|
2016-06-07 14:55:35 +00:00
|
|
|
// If the addr for the service is ":port", then we fall back
|
|
|
|
// to Nomad's default address resolution protocol.
|
|
|
|
//
|
|
|
|
// TODO(sean@): This should poll Consul to figure out what
|
|
|
|
// its advertise address is and use that in order to handle
|
|
|
|
// the case where there is something funky like NAT on this
|
|
|
|
// host. For now we just use the BindAddr if set, otherwise
|
|
|
|
// we fall back to a loopback addr.
|
2016-05-15 00:08:19 +00:00
|
|
|
if host == "" {
|
2016-06-07 14:55:35 +00:00
|
|
|
if a.config.BindAddr != "" {
|
2016-06-08 06:31:19 +00:00
|
|
|
host = a.config.BindAddr
|
2016-06-07 14:55:35 +00:00
|
|
|
} else {
|
|
|
|
host = "127.0.0.1"
|
|
|
|
}
|
2016-05-15 00:08:19 +00:00
|
|
|
}
|
2016-05-11 22:24:37 +00:00
|
|
|
p, err := strconv.Atoi(port)
|
|
|
|
if err != nil {
|
2016-06-07 14:55:35 +00:00
|
|
|
return host, 0
|
2016-05-11 22:24:37 +00:00
|
|
|
}
|
|
|
|
return host, p
|
|
|
|
})
|
|
|
|
|
2016-06-10 01:12:02 +00:00
|
|
|
return nil
|
2016-05-11 22:24:37 +00:00
|
|
|
}
|