github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/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 "sync" 11 12 "github.com/hashicorp/nomad/client" 13 "github.com/hashicorp/nomad/nomad" 14 "github.com/hashicorp/nomad/nomad/structs" 15 ) 16 17 // Agent is a long running daemon that is used to run both 18 // clients and servers. Servers are responsible for managing 19 // state and making scheduling decisions. Clients can be 20 // scheduled to, and are responsible for interfacing with 21 // servers to run allocations. 22 type Agent struct { 23 config *Config 24 logger *log.Logger 25 logOutput io.Writer 26 27 server *nomad.Server 28 client *client.Client 29 30 shutdown bool 31 shutdownCh chan struct{} 32 shutdownLock sync.Mutex 33 } 34 35 // NewAgent is used to create a new agent with the given configuration 36 func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) { 37 // Ensure we have a log sink 38 if logOutput == nil { 39 logOutput = os.Stderr 40 } 41 42 a := &Agent{ 43 config: config, 44 logger: log.New(logOutput, "", log.LstdFlags), 45 logOutput: logOutput, 46 shutdownCh: make(chan struct{}), 47 } 48 49 if err := a.setupServer(); err != nil { 50 return nil, err 51 } 52 if err := a.setupClient(); err != nil { 53 return nil, err 54 } 55 if a.client == nil && a.server == nil { 56 return nil, fmt.Errorf("must have at least client or server mode enabled") 57 } 58 return a, nil 59 } 60 61 // serverConfig is used to generate a new server configuration struct 62 // for initializing a nomad server. 63 func (a *Agent) serverConfig() (*nomad.Config, error) { 64 conf := a.config.NomadConfig 65 if conf == nil { 66 conf = nomad.DefaultConfig() 67 } 68 conf.LogOutput = a.logOutput 69 conf.DevMode = a.config.DevMode 70 conf.Build = fmt.Sprintf("%s%s", a.config.Version, a.config.VersionPrerelease) 71 if a.config.Region != "" { 72 conf.Region = a.config.Region 73 } 74 if a.config.Datacenter != "" { 75 conf.Datacenter = a.config.Datacenter 76 } 77 if a.config.NodeName != "" { 78 conf.NodeName = a.config.NodeName 79 } 80 if a.config.Server.BootstrapExpect > 0 { 81 if a.config.Server.BootstrapExpect == 1 { 82 conf.Bootstrap = true 83 } else { 84 conf.BootstrapExpect = a.config.Server.BootstrapExpect 85 } 86 } 87 if a.config.DataDir != "" { 88 conf.DataDir = filepath.Join(a.config.DataDir, "server") 89 } 90 if a.config.Server.DataDir != "" { 91 conf.DataDir = a.config.Server.DataDir 92 } 93 if a.config.Server.ProtocolVersion != 0 { 94 conf.ProtocolVersion = uint8(a.config.Server.ProtocolVersion) 95 } 96 if a.config.Server.NumSchedulers != 0 { 97 conf.NumSchedulers = a.config.Server.NumSchedulers 98 } 99 if len(a.config.Server.EnabledSchedulers) != 0 { 100 conf.EnabledSchedulers = a.config.Server.EnabledSchedulers 101 } 102 103 // Set up the advertise addrs 104 if addr := a.config.AdvertiseAddrs.Serf; addr != "" { 105 serfAddr, err := net.ResolveTCPAddr("tcp", addr) 106 if err != nil { 107 return nil, fmt.Errorf("error resolving serf advertise address: %s", err) 108 } 109 conf.SerfConfig.MemberlistConfig.AdvertiseAddr = serfAddr.IP.String() 110 conf.SerfConfig.MemberlistConfig.AdvertisePort = serfAddr.Port 111 } 112 if addr := a.config.AdvertiseAddrs.RPC; addr != "" { 113 rpcAddr, err := net.ResolveTCPAddr("tcp", addr) 114 if err != nil { 115 return nil, fmt.Errorf("error resolving rpc advertise address: %s", err) 116 } 117 conf.RPCAdvertise = rpcAddr 118 } 119 120 // Set up the bind addresses 121 if addr := a.config.BindAddr; addr != "" { 122 conf.RPCAddr.IP = net.ParseIP(addr) 123 conf.SerfConfig.MemberlistConfig.BindAddr = addr 124 } 125 if addr := a.config.Addresses.RPC; addr != "" { 126 conf.RPCAddr.IP = net.ParseIP(addr) 127 } 128 if addr := a.config.Addresses.Serf; addr != "" { 129 conf.SerfConfig.MemberlistConfig.BindAddr = addr 130 } 131 132 // Set up the ports 133 if port := a.config.Ports.RPC; port != 0 { 134 conf.RPCAddr.Port = port 135 } 136 if port := a.config.Ports.Serf; port != 0 { 137 conf.SerfConfig.MemberlistConfig.BindPort = port 138 } 139 140 return conf, nil 141 } 142 143 // setupServer is used to setup the server if enabled 144 func (a *Agent) setupServer() error { 145 if !a.config.Server.Enabled { 146 return nil 147 } 148 149 // Setup the configuration 150 conf, err := a.serverConfig() 151 if err != nil { 152 return fmt.Errorf("server config setup failed: %s", err) 153 } 154 155 // Create the server 156 server, err := nomad.NewServer(conf) 157 if err != nil { 158 return fmt.Errorf("server setup failed: %v", err) 159 } 160 161 a.server = server 162 return nil 163 } 164 165 // setupClient is used to setup the client if enabled 166 func (a *Agent) setupClient() error { 167 if !a.config.Client.Enabled { 168 return nil 169 } 170 171 // Setup the configuration 172 conf := a.config.ClientConfig 173 if conf == nil { 174 conf = client.DefaultConfig() 175 } 176 if a.server != nil { 177 conf.RPCHandler = a.server 178 } 179 conf.LogOutput = a.logOutput 180 conf.DevMode = a.config.DevMode 181 if a.config.Region != "" { 182 conf.Region = a.config.Region 183 } 184 if a.config.DataDir != "" { 185 conf.StateDir = filepath.Join(a.config.DataDir, "client") 186 conf.AllocDir = filepath.Join(a.config.DataDir, "alloc") 187 } 188 if a.config.Client.StateDir != "" { 189 conf.StateDir = a.config.Client.StateDir 190 } 191 if a.config.Client.AllocDir != "" { 192 conf.AllocDir = a.config.Client.AllocDir 193 } 194 conf.Servers = a.config.Client.Servers 195 if a.config.Client.NetworkInterface != "" { 196 conf.NetworkInterface = a.config.Client.NetworkInterface 197 } 198 conf.Options = a.config.Client.Options 199 if a.config.Client.NetworkSpeed != 0 { 200 conf.NetworkSpeed = a.config.Client.NetworkSpeed 201 } 202 203 // Setup the node 204 conf.Node = new(structs.Node) 205 conf.Node.Datacenter = a.config.Datacenter 206 conf.Node.Name = a.config.NodeName 207 conf.Node.ID = a.config.Client.NodeID 208 conf.Node.Meta = a.config.Client.Meta 209 conf.Node.NodeClass = a.config.Client.NodeClass 210 211 // Create the client 212 client, err := client.NewClient(conf) 213 if err != nil { 214 return fmt.Errorf("client setup failed: %v", err) 215 } 216 a.client = client 217 return nil 218 } 219 220 // Leave is used gracefully exit. Clients will inform servers 221 // of their departure so that allocations can be rescheduled. 222 func (a *Agent) Leave() error { 223 if a.client != nil { 224 if err := a.client.Leave(); err != nil { 225 a.logger.Printf("[ERR] agent: client leave failed: %v", err) 226 } 227 } 228 if a.server != nil { 229 if err := a.server.Leave(); err != nil { 230 a.logger.Printf("[ERR] agent: server leave failed: %v", err) 231 } 232 } 233 return nil 234 } 235 236 // Shutdown is used to terminate the agent. 237 func (a *Agent) Shutdown() error { 238 a.shutdownLock.Lock() 239 defer a.shutdownLock.Unlock() 240 241 if a.shutdown { 242 return nil 243 } 244 245 a.logger.Println("[INFO] agent: requesting shutdown") 246 if a.client != nil { 247 if err := a.client.Shutdown(); err != nil { 248 a.logger.Printf("[ERR] agent: client shutdown failed: %v", err) 249 } 250 } 251 if a.server != nil { 252 if err := a.server.Shutdown(); err != nil { 253 a.logger.Printf("[ERR] agent: server shutdown failed: %v", err) 254 } 255 } 256 257 a.logger.Println("[INFO] agent: shutdown complete") 258 a.shutdown = true 259 close(a.shutdownCh) 260 return nil 261 } 262 263 // RPC is used to make an RPC call to the Nomad servers 264 func (a *Agent) RPC(method string, args interface{}, reply interface{}) error { 265 if a.server != nil { 266 return a.server.RPC(method, args, reply) 267 } 268 return a.client.RPC(method, args, reply) 269 } 270 271 // Client returns the configured client or nil 272 func (a *Agent) Client() *client.Client { 273 return a.client 274 } 275 276 // Server returns the configured server or nil 277 func (a *Agent) Server() *nomad.Server { 278 return a.server 279 } 280 281 // Stats is used to return statistics for debugging and insight 282 // for various sub-systems 283 func (a *Agent) Stats() map[string]map[string]string { 284 stats := make(map[string]map[string]string) 285 if a.server != nil { 286 subStat := a.server.Stats() 287 for k, v := range subStat { 288 stats[k] = v 289 } 290 } 291 if a.client != nil { 292 subStat := a.client.Stats() 293 for k, v := range subStat { 294 stats[k] = v 295 } 296 } 297 return stats 298 }