github.com/diptanu/nomad@v0.5.7-0.20170516172507-d72e86cbe3d9/command/agent/agent_test.go (about) 1 package agent 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "net" 9 "os" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/hashicorp/nomad/helper" 15 "github.com/hashicorp/nomad/nomad" 16 sconfig "github.com/hashicorp/nomad/nomad/structs/config" 17 ) 18 19 func getPort() int { 20 addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0") 21 if err != nil { 22 panic(err) 23 } 24 25 l, err := net.ListenTCP("tcp", addr) 26 if err != nil { 27 panic(err) 28 } 29 defer l.Close() 30 return l.Addr().(*net.TCPAddr).Port 31 } 32 33 func tmpDir(t testing.TB) string { 34 dir, err := ioutil.TempDir("", "nomad") 35 if err != nil { 36 t.Fatalf("err: %v", err) 37 } 38 return dir 39 } 40 41 func makeAgent(t testing.TB, cb func(*Config)) (string, *Agent) { 42 dir := tmpDir(t) 43 conf := DevConfig() 44 45 // Customize the server configuration 46 config := nomad.DefaultConfig() 47 conf.NomadConfig = config 48 49 // Set the data_dir 50 conf.DataDir = dir 51 conf.NomadConfig.DataDir = dir 52 53 // Bind and set ports 54 conf.BindAddr = "127.0.0.1" 55 conf.Ports = &Ports{ 56 HTTP: getPort(), 57 RPC: getPort(), 58 Serf: getPort(), 59 } 60 conf.NodeName = fmt.Sprintf("Node %d", conf.Ports.RPC) 61 conf.Consul = sconfig.DefaultConsulConfig() 62 conf.Vault.Enabled = new(bool) 63 64 // Tighten the Serf timing 65 config.SerfConfig.MemberlistConfig.SuspicionMult = 2 66 config.SerfConfig.MemberlistConfig.RetransmitMult = 2 67 config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond 68 config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond 69 config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond 70 71 // Tighten the Raft timing 72 config.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond 73 config.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond 74 config.RaftConfig.ElectionTimeout = 40 * time.Millisecond 75 config.RaftConfig.StartAsLeader = true 76 config.RaftTimeout = 500 * time.Millisecond 77 78 if cb != nil { 79 cb(conf) 80 } 81 82 if err := conf.normalizeAddrs(); err != nil { 83 t.Fatalf("error normalizing config: %v", err) 84 } 85 agent, err := NewAgent(conf, os.Stderr) 86 if err != nil { 87 os.RemoveAll(dir) 88 t.Fatalf("err: %v", err) 89 } 90 return dir, agent 91 } 92 93 func TestAgent_RPCPing(t *testing.T) { 94 dir, agent := makeAgent(t, nil) 95 defer os.RemoveAll(dir) 96 defer agent.Shutdown() 97 98 var out struct{} 99 if err := agent.RPC("Status.Ping", struct{}{}, &out); err != nil { 100 t.Fatalf("err: %v", err) 101 } 102 } 103 104 func TestAgent_ServerConfig(t *testing.T) { 105 conf := DefaultConfig() 106 conf.DevMode = true // allow localhost for advertise addrs 107 a := &Agent{config: conf} 108 109 conf.AdvertiseAddrs.Serf = "127.0.0.1:4000" 110 conf.AdvertiseAddrs.RPC = "127.0.0.1:4001" 111 conf.AdvertiseAddrs.HTTP = "10.10.11.1:4005" 112 113 // Parses the advertise addrs correctly 114 if err := conf.normalizeAddrs(); err != nil { 115 t.Fatalf("error normalizing config: %v", err) 116 } 117 out, err := a.serverConfig() 118 if err != nil { 119 t.Fatalf("err: %s", err) 120 } 121 serfAddr := out.SerfConfig.MemberlistConfig.AdvertiseAddr 122 if serfAddr != "127.0.0.1" { 123 t.Fatalf("expect 127.0.0.1, got: %s", serfAddr) 124 } 125 serfPort := out.SerfConfig.MemberlistConfig.AdvertisePort 126 if serfPort != 4000 { 127 t.Fatalf("expected 4000, got: %d", serfPort) 128 } 129 130 // Assert addresses weren't changed 131 if addr := conf.AdvertiseAddrs.RPC; addr != "127.0.0.1:4001" { 132 t.Fatalf("bad rpc advertise addr: %#v", addr) 133 } 134 if addr := conf.AdvertiseAddrs.HTTP; addr != "10.10.11.1:4005" { 135 t.Fatalf("expect 10.11.11.1:4005, got: %v", addr) 136 } 137 if addr := conf.Addresses.RPC; addr != "0.0.0.0" { 138 t.Fatalf("expect 0.0.0.0, got: %v", addr) 139 } 140 141 // Sets up the ports properly 142 conf.Addresses.RPC = "" 143 conf.Addresses.Serf = "" 144 conf.Ports.RPC = 4003 145 conf.Ports.Serf = 4004 146 147 if err := conf.normalizeAddrs(); err != nil { 148 t.Fatalf("error normalizing config: %v", err) 149 } 150 out, err = a.serverConfig() 151 if err != nil { 152 t.Fatalf("err: %s", err) 153 } 154 if addr := out.RPCAddr.Port; addr != 4003 { 155 t.Fatalf("expect 4003, got: %d", out.RPCAddr.Port) 156 } 157 if port := out.SerfConfig.MemberlistConfig.BindPort; port != 4004 { 158 t.Fatalf("expect 4004, got: %d", port) 159 } 160 161 // Prefers advertise over bind addr 162 conf.BindAddr = "127.0.0.3" 163 conf.Addresses.HTTP = "127.0.0.2" 164 conf.Addresses.RPC = "127.0.0.2" 165 conf.Addresses.Serf = "127.0.0.2" 166 conf.AdvertiseAddrs.HTTP = "10.0.0.10" 167 conf.AdvertiseAddrs.RPC = "" 168 conf.AdvertiseAddrs.Serf = "10.0.0.12:4004" 169 170 if err := conf.normalizeAddrs(); err != nil { 171 t.Fatalf("error normalizing config: %v", err) 172 } 173 out, err = a.serverConfig() 174 fmt.Println(conf.Addresses.RPC) 175 if err != nil { 176 t.Fatalf("err: %s", err) 177 } 178 if addr := out.RPCAddr.IP.String(); addr != "127.0.0.2" { 179 t.Fatalf("expect 127.0.0.2, got: %s", addr) 180 } 181 if port := out.RPCAddr.Port; port != 4003 { 182 t.Fatalf("expect 4647, got: %d", port) 183 } 184 if addr := out.SerfConfig.MemberlistConfig.BindAddr; addr != "127.0.0.2" { 185 t.Fatalf("expect 127.0.0.2, got: %s", addr) 186 } 187 if port := out.SerfConfig.MemberlistConfig.BindPort; port != 4004 { 188 t.Fatalf("expect 4648, got: %d", port) 189 } 190 if addr := conf.Addresses.HTTP; addr != "127.0.0.2" { 191 t.Fatalf("expect 127.0.0.2, got: %s", addr) 192 } 193 if addr := conf.Addresses.RPC; addr != "127.0.0.2" { 194 t.Fatalf("expect 127.0.0.2, got: %s", addr) 195 } 196 if addr := conf.Addresses.Serf; addr != "127.0.0.2" { 197 t.Fatalf("expect 10.0.0.12, got: %s", addr) 198 } 199 if addr := conf.normalizedAddrs.HTTP; addr != "127.0.0.2:4646" { 200 t.Fatalf("expect 127.0.0.2:4646, got: %s", addr) 201 } 202 if addr := conf.normalizedAddrs.RPC; addr != "127.0.0.2:4003" { 203 t.Fatalf("expect 127.0.0.2:4003, got: %s", addr) 204 } 205 if addr := conf.normalizedAddrs.Serf; addr != "127.0.0.2:4004" { 206 t.Fatalf("expect 10.0.0.12:4004, got: %s", addr) 207 } 208 if addr := conf.AdvertiseAddrs.HTTP; addr != "10.0.0.10:4646" { 209 t.Fatalf("expect 10.0.0.10:4646, got: %s", addr) 210 } 211 if addr := conf.AdvertiseAddrs.RPC; addr != "127.0.0.2:4003" { 212 t.Fatalf("expect 127.0.0.2:4003, got: %s", addr) 213 } 214 if addr := conf.AdvertiseAddrs.Serf; addr != "10.0.0.12:4004" { 215 t.Fatalf("expect 10.0.0.12:4004, got: %s", addr) 216 } 217 218 conf.Server.NodeGCThreshold = "42g" 219 if err := conf.normalizeAddrs(); err != nil { 220 t.Fatalf("error normalizing config: %v", err) 221 } 222 out, err = a.serverConfig() 223 if err == nil || !strings.Contains(err.Error(), "unknown unit") { 224 t.Fatalf("expected unknown unit error, got: %#v", err) 225 } 226 227 conf.Server.NodeGCThreshold = "10s" 228 if err := conf.normalizeAddrs(); err != nil { 229 t.Fatalf("error normalizing config: %v", err) 230 } 231 out, err = a.serverConfig() 232 if threshold := out.NodeGCThreshold; threshold != time.Second*10 { 233 t.Fatalf("expect 10s, got: %s", threshold) 234 } 235 236 conf.Server.HeartbeatGrace = "42g" 237 if err := conf.normalizeAddrs(); err != nil { 238 t.Fatalf("error normalizing config: %v", err) 239 } 240 out, err = a.serverConfig() 241 if err == nil || !strings.Contains(err.Error(), "unknown unit") { 242 t.Fatalf("expected unknown unit error, got: %#v", err) 243 } 244 245 conf.Server.HeartbeatGrace = "37s" 246 if err := conf.normalizeAddrs(); err != nil { 247 t.Fatalf("error normalizing config: %v", err) 248 } 249 out, err = a.serverConfig() 250 if threshold := out.HeartbeatGrace; threshold != time.Second*37 { 251 t.Fatalf("expect 37s, got: %s", threshold) 252 } 253 254 // Defaults to the global bind addr 255 conf.Addresses.RPC = "" 256 conf.Addresses.Serf = "" 257 conf.Addresses.HTTP = "" 258 conf.AdvertiseAddrs.RPC = "" 259 conf.AdvertiseAddrs.HTTP = "" 260 conf.AdvertiseAddrs.Serf = "" 261 conf.Ports.HTTP = 4646 262 conf.Ports.RPC = 4647 263 conf.Ports.Serf = 4648 264 if err := conf.normalizeAddrs(); err != nil { 265 t.Fatalf("error normalizing config: %v", err) 266 } 267 out, err = a.serverConfig() 268 if err != nil { 269 t.Fatalf("err: %s", err) 270 } 271 if addr := out.RPCAddr.IP.String(); addr != "127.0.0.3" { 272 t.Fatalf("expect 127.0.0.3, got: %s", addr) 273 } 274 if addr := out.SerfConfig.MemberlistConfig.BindAddr; addr != "127.0.0.3" { 275 t.Fatalf("expect 127.0.0.3, got: %s", addr) 276 } 277 if addr := conf.Addresses.HTTP; addr != "127.0.0.3" { 278 t.Fatalf("expect 127.0.0.3, got: %s", addr) 279 } 280 if addr := conf.Addresses.RPC; addr != "127.0.0.3" { 281 t.Fatalf("expect 127.0.0.3, got: %s", addr) 282 } 283 if addr := conf.Addresses.Serf; addr != "127.0.0.3" { 284 t.Fatalf("expect 127.0.0.3, got: %s", addr) 285 } 286 if addr := conf.normalizedAddrs.HTTP; addr != "127.0.0.3:4646" { 287 t.Fatalf("expect 127.0.0.3:4646, got: %s", addr) 288 } 289 if addr := conf.normalizedAddrs.RPC; addr != "127.0.0.3:4647" { 290 t.Fatalf("expect 127.0.0.3:4647, got: %s", addr) 291 } 292 if addr := conf.normalizedAddrs.Serf; addr != "127.0.0.3:4648" { 293 t.Fatalf("expect 127.0.0.3:4648, got: %s", addr) 294 } 295 296 // Properly handles the bootstrap flags 297 conf.Server.BootstrapExpect = 1 298 out, err = a.serverConfig() 299 if err != nil { 300 t.Fatalf("err: %s", err) 301 } 302 if !out.Bootstrap { 303 t.Fatalf("should have set bootstrap mode") 304 } 305 if out.BootstrapExpect != 0 { 306 t.Fatalf("boostrap expect should be 0") 307 } 308 309 conf.Server.BootstrapExpect = 3 310 out, err = a.serverConfig() 311 if err != nil { 312 t.Fatalf("err: %s", err) 313 } 314 if out.Bootstrap { 315 t.Fatalf("bootstrap mode should be disabled") 316 } 317 if out.BootstrapExpect != 3 { 318 t.Fatalf("should have bootstrap-expect = 3") 319 } 320 } 321 322 func TestAgent_ClientConfig(t *testing.T) { 323 conf := DefaultConfig() 324 conf.Client.Enabled = true 325 326 // For Clients HTTP and RPC must be set (Serf can be skipped) 327 conf.Addresses.HTTP = "169.254.0.1" 328 conf.Addresses.RPC = "169.254.0.1" 329 conf.Ports.HTTP = 5678 330 a := &Agent{config: conf} 331 332 if err := conf.normalizeAddrs(); err != nil { 333 t.Fatalf("error normalizing config: %v", err) 334 } 335 c, err := a.clientConfig() 336 if err != nil { 337 t.Fatalf("got err: %v", err) 338 } 339 340 expectedHttpAddr := "169.254.0.1:5678" 341 if c.Node.HTTPAddr != expectedHttpAddr { 342 t.Fatalf("Expected http addr: %v, got: %v", expectedHttpAddr, c.Node.HTTPAddr) 343 } 344 345 conf = DefaultConfig() 346 conf.DevMode = true 347 a = &Agent{config: conf} 348 conf.Client.Enabled = true 349 conf.Addresses.HTTP = "169.254.0.1" 350 351 if err := conf.normalizeAddrs(); err != nil { 352 t.Fatalf("error normalizing config: %v", err) 353 } 354 c, err = a.clientConfig() 355 if err != nil { 356 t.Fatalf("got err: %v", err) 357 } 358 359 expectedHttpAddr = "169.254.0.1:4646" 360 if c.Node.HTTPAddr != expectedHttpAddr { 361 t.Fatalf("Expected http addr: %v, got: %v", expectedHttpAddr, c.Node.HTTPAddr) 362 } 363 } 364 365 // TestAgent_HTTPCheck asserts Agent.agentHTTPCheck properly alters the HTTP 366 // API health check depending on configuration. 367 func TestAgent_HTTPCheck(t *testing.T) { 368 logger := log.New(ioutil.Discard, "", 0) 369 if testing.Verbose() { 370 logger = log.New(os.Stdout, "[TestAgent_HTTPCheck] ", log.Lshortfile) 371 } 372 agent := func() *Agent { 373 return &Agent{ 374 logger: logger, 375 config: &Config{ 376 AdvertiseAddrs: &AdvertiseAddrs{HTTP: "advertise:4646"}, 377 normalizedAddrs: &Addresses{HTTP: "normalized:4646"}, 378 Consul: &sconfig.ConsulConfig{ 379 ChecksUseAdvertise: helper.BoolToPtr(false), 380 }, 381 TLSConfig: &sconfig.TLSConfig{EnableHTTP: false}, 382 }, 383 } 384 } 385 386 t.Run("Plain HTTP Check", func(t *testing.T) { 387 a := agent() 388 check := a.agentHTTPCheck() 389 if check == nil { 390 t.Fatalf("expected non-nil check") 391 } 392 if check.Type != "http" { 393 t.Errorf("expected http check not: %q", check.Type) 394 } 395 if expected := "/v1/agent/servers"; check.Path != expected { 396 t.Errorf("expected %q path not: %q", expected, check.Path) 397 } 398 if check.Protocol != "http" { 399 t.Errorf("expected http proto not: %q", check.Protocol) 400 } 401 if expected := a.config.normalizedAddrs.HTTP; check.PortLabel != expected { 402 t.Errorf("expected normalized addr not %q", check.PortLabel) 403 } 404 }) 405 406 t.Run("Plain HTTP + ChecksUseAdvertise", func(t *testing.T) { 407 a := agent() 408 a.config.Consul.ChecksUseAdvertise = helper.BoolToPtr(true) 409 check := a.agentHTTPCheck() 410 if check == nil { 411 t.Fatalf("expected non-nil check") 412 } 413 if expected := a.config.AdvertiseAddrs.HTTP; check.PortLabel != expected { 414 t.Errorf("expected advertise addr not %q", check.PortLabel) 415 } 416 }) 417 418 t.Run("HTTPS + consulSupportsTLSSkipVerify", func(t *testing.T) { 419 a := agent() 420 a.consulSupportsTLSSkipVerify = true 421 a.config.TLSConfig.EnableHTTP = true 422 423 check := a.agentHTTPCheck() 424 if check == nil { 425 t.Fatalf("expected non-nil check") 426 } 427 if !check.TLSSkipVerify { 428 t.Errorf("expected tls skip verify") 429 } 430 if check.Protocol != "https" { 431 t.Errorf("expected https not: %q", check.Protocol) 432 } 433 }) 434 435 t.Run("HTTPS w/o TLSSkipVerify", func(t *testing.T) { 436 a := agent() 437 a.consulSupportsTLSSkipVerify = false 438 a.config.TLSConfig.EnableHTTP = true 439 440 if check := a.agentHTTPCheck(); check != nil { 441 t.Fatalf("expected nil check not: %#v", check) 442 } 443 }) 444 445 t.Run("HTTPS + VerifyHTTPSClient", func(t *testing.T) { 446 a := agent() 447 a.consulSupportsTLSSkipVerify = true 448 a.config.TLSConfig.EnableHTTP = true 449 a.config.TLSConfig.VerifyHTTPSClient = true 450 451 if check := a.agentHTTPCheck(); check != nil { 452 t.Fatalf("expected nil check not: %#v", check) 453 } 454 }) 455 } 456 457 func TestAgent_ConsulSupportsTLSSkipVerify(t *testing.T) { 458 assertSupport := func(expected bool, blob string) { 459 self := map[string]map[string]interface{}{} 460 if err := json.Unmarshal([]byte("{"+blob+"}"), &self); err != nil { 461 t.Fatalf("invalid json: %v", err) 462 } 463 actual := consulSupportsTLSSkipVerify(self) 464 if actual != expected { 465 t.Errorf("expected %t but got %t for:\n%s\n", expected, actual, blob) 466 } 467 } 468 469 // 0.6.4 470 assertSupport(false, `"Member": { 471 "Addr": "127.0.0.1", 472 "DelegateCur": 4, 473 "DelegateMax": 4, 474 "DelegateMin": 2, 475 "Name": "rusty", 476 "Port": 8301, 477 "ProtocolCur": 2, 478 "ProtocolMax": 3, 479 "ProtocolMin": 1, 480 "Status": 1, 481 "Tags": { 482 "build": "0.6.4:26a0ef8c", 483 "dc": "dc1", 484 "port": "8300", 485 "role": "consul", 486 "vsn": "2", 487 "vsn_max": "3", 488 "vsn_min": "1" 489 }}`) 490 491 // 0.7.0 492 assertSupport(false, `"Member": { 493 "Addr": "127.0.0.1", 494 "DelegateCur": 4, 495 "DelegateMax": 4, 496 "DelegateMin": 2, 497 "Name": "rusty", 498 "Port": 8301, 499 "ProtocolCur": 2, 500 "ProtocolMax": 4, 501 "ProtocolMin": 1, 502 "Status": 1, 503 "Tags": { 504 "build": "0.7.0:'a189091", 505 "dc": "dc1", 506 "port": "8300", 507 "role": "consul", 508 "vsn": "2", 509 "vsn_max": "3", 510 "vsn_min": "2" 511 }}`) 512 513 // 0.7.2 514 assertSupport(true, `"Member": { 515 "Addr": "127.0.0.1", 516 "DelegateCur": 4, 517 "DelegateMax": 4, 518 "DelegateMin": 2, 519 "Name": "rusty", 520 "Port": 8301, 521 "ProtocolCur": 2, 522 "ProtocolMax": 5, 523 "ProtocolMin": 1, 524 "Status": 1, 525 "Tags": { 526 "build": "0.7.2:'a9afa0c", 527 "dc": "dc1", 528 "port": "8300", 529 "role": "consul", 530 "vsn": "2", 531 "vsn_max": "3", 532 "vsn_min": "2" 533 }}`) 534 535 // 0.8.1 536 assertSupport(true, `"Member": { 537 "Addr": "127.0.0.1", 538 "DelegateCur": 4, 539 "DelegateMax": 5, 540 "DelegateMin": 2, 541 "Name": "rusty", 542 "Port": 8301, 543 "ProtocolCur": 2, 544 "ProtocolMax": 5, 545 "ProtocolMin": 1, 546 "Status": 1, 547 "Tags": { 548 "build": "0.8.1:'e9ca44d", 549 "dc": "dc1", 550 "id": "3ddc1b59-460e-a100-1d5c-ce3972122664", 551 "port": "8300", 552 "raft_vsn": "2", 553 "role": "consul", 554 "vsn": "2", 555 "vsn_max": "3", 556 "vsn_min": "2", 557 "wan_join_port": "8302" 558 }}`) 559 }