github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/client_test.go (about) 1 package client 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "math/rand" 11 "net" 12 "os" 13 "path/filepath" 14 "testing" 15 "time" 16 17 memdb "github.com/hashicorp/go-memdb" 18 "github.com/hashicorp/nomad/client/config" 19 "github.com/hashicorp/nomad/client/fingerprint" 20 "github.com/hashicorp/nomad/command/agent/consul" 21 "github.com/hashicorp/nomad/helper" 22 "github.com/hashicorp/nomad/nomad" 23 "github.com/hashicorp/nomad/nomad/mock" 24 "github.com/hashicorp/nomad/nomad/structs" 25 nconfig "github.com/hashicorp/nomad/nomad/structs/config" 26 "github.com/hashicorp/nomad/testutil" 27 "github.com/mitchellh/hashstructure" 28 29 ctestutil "github.com/hashicorp/nomad/client/testutil" 30 ) 31 32 func getPort() int { 33 return 1030 + int(rand.Int31n(6440)) 34 } 35 36 func testServer(t *testing.T, cb func(*nomad.Config)) (*nomad.Server, string) { 37 // Setup the default settings 38 config := nomad.DefaultConfig() 39 config.VaultConfig.Enabled = helper.BoolToPtr(false) 40 config.Build = "unittest" 41 config.DevMode = true 42 43 // Tighten the Serf timing 44 config.SerfConfig.MemberlistConfig.BindAddr = "127.0.0.1" 45 config.SerfConfig.MemberlistConfig.SuspicionMult = 2 46 config.SerfConfig.MemberlistConfig.RetransmitMult = 2 47 config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond 48 config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond 49 config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond 50 51 // Tighten the Raft timing 52 config.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond 53 config.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond 54 config.RaftConfig.ElectionTimeout = 40 * time.Millisecond 55 config.RaftConfig.StartAsLeader = true 56 config.RaftTimeout = 500 * time.Millisecond 57 58 logger := log.New(config.LogOutput, "", log.LstdFlags) 59 catalog := consul.NewMockCatalog(logger) 60 61 // Invoke the callback if any 62 if cb != nil { 63 cb(config) 64 } 65 66 for i := 10; i >= 0; i-- { 67 config.RPCAddr = &net.TCPAddr{ 68 IP: []byte{127, 0, 0, 1}, 69 Port: getPort(), 70 } 71 config.NodeName = fmt.Sprintf("Node %d", config.RPCAddr.Port) 72 config.SerfConfig.MemberlistConfig.BindPort = getPort() 73 74 // Create server 75 server, err := nomad.NewServer(config, catalog, logger) 76 if err == nil { 77 return server, config.RPCAddr.String() 78 } else if i == 0 { 79 t.Fatalf("err: %v", err) 80 } else { 81 wait := time.Duration(rand.Int31n(2000)) * time.Millisecond 82 time.Sleep(wait) 83 } 84 } 85 return nil, "" 86 } 87 88 func testClient(t *testing.T, cb func(c *config.Config)) *Client { 89 conf := config.DefaultConfig() 90 conf.VaultConfig.Enabled = helper.BoolToPtr(false) 91 conf.DevMode = true 92 conf.Node = &structs.Node{ 93 Reserved: &structs.Resources{ 94 DiskMB: 0, 95 }, 96 } 97 98 // Tighten the fingerprinter timeouts 99 if conf.Options == nil { 100 conf.Options = make(map[string]string) 101 } 102 conf.Options[fingerprint.TightenNetworkTimeoutsConfig] = "true" 103 104 if cb != nil { 105 cb(conf) 106 } 107 108 logger := log.New(conf.LogOutput, "", log.LstdFlags) 109 catalog := consul.NewMockCatalog(logger) 110 mockService := newMockConsulServiceClient() 111 mockService.logger = logger 112 client, err := NewClient(conf, catalog, mockService, logger) 113 if err != nil { 114 t.Fatalf("err: %v", err) 115 } 116 return client 117 } 118 119 func TestClient_StartStop(t *testing.T) { 120 t.Parallel() 121 client := testClient(t, nil) 122 if err := client.Shutdown(); err != nil { 123 t.Fatalf("err: %v", err) 124 } 125 } 126 127 func TestClient_RPC(t *testing.T) { 128 t.Parallel() 129 s1, addr := testServer(t, nil) 130 defer s1.Shutdown() 131 132 c1 := testClient(t, func(c *config.Config) { 133 c.Servers = []string{addr} 134 }) 135 defer c1.Shutdown() 136 137 // RPC should succeed 138 testutil.WaitForResult(func() (bool, error) { 139 var out struct{} 140 err := c1.RPC("Status.Ping", struct{}{}, &out) 141 return err == nil, err 142 }, func(err error) { 143 t.Fatalf("err: %v", err) 144 }) 145 } 146 147 func TestClient_RPC_Passthrough(t *testing.T) { 148 t.Parallel() 149 s1, _ := testServer(t, nil) 150 defer s1.Shutdown() 151 152 c1 := testClient(t, func(c *config.Config) { 153 c.RPCHandler = s1 154 }) 155 defer c1.Shutdown() 156 157 // RPC should succeed 158 testutil.WaitForResult(func() (bool, error) { 159 var out struct{} 160 err := c1.RPC("Status.Ping", struct{}{}, &out) 161 return err == nil, err 162 }, func(err error) { 163 t.Fatalf("err: %v", err) 164 }) 165 } 166 167 func TestClient_Fingerprint(t *testing.T) { 168 t.Parallel() 169 c := testClient(t, nil) 170 defer c.Shutdown() 171 172 // Ensure kernel and arch are always present 173 node := c.Node() 174 if node.Attributes["kernel.name"] == "" { 175 t.Fatalf("missing kernel.name") 176 } 177 if node.Attributes["cpu.arch"] == "" { 178 t.Fatalf("missing cpu arch") 179 } 180 } 181 182 func TestClient_HasNodeChanged(t *testing.T) { 183 t.Parallel() 184 c := testClient(t, nil) 185 defer c.Shutdown() 186 187 node := c.Node() 188 attrHash, err := hashstructure.Hash(node.Attributes, nil) 189 if err != nil { 190 c.logger.Printf("[DEBUG] client: unable to calculate node attributes hash: %v", err) 191 } 192 // Calculate node meta map hash 193 metaHash, err := hashstructure.Hash(node.Meta, nil) 194 if err != nil { 195 c.logger.Printf("[DEBUG] client: unable to calculate node meta hash: %v", err) 196 } 197 if changed, _, _ := c.hasNodeChanged(attrHash, metaHash); changed { 198 t.Fatalf("Unexpected hash change.") 199 } 200 201 // Change node attribute 202 node.Attributes["arch"] = "xyz_86" 203 if changed, newAttrHash, _ := c.hasNodeChanged(attrHash, metaHash); !changed { 204 t.Fatalf("Expected hash change in attributes: %d vs %d", attrHash, newAttrHash) 205 } 206 207 // Change node meta map 208 node.Meta["foo"] = "bar" 209 if changed, _, newMetaHash := c.hasNodeChanged(attrHash, metaHash); !changed { 210 t.Fatalf("Expected hash change in meta map: %d vs %d", metaHash, newMetaHash) 211 } 212 } 213 214 func TestClient_Fingerprint_InWhitelist(t *testing.T) { 215 t.Parallel() 216 c := testClient(t, func(c *config.Config) { 217 if c.Options == nil { 218 c.Options = make(map[string]string) 219 } 220 221 // Weird spacing to test trimming. Whitelist all modules expect cpu. 222 c.Options["fingerprint.whitelist"] = " arch, consul,cpu,env_aws,env_gce,host,memory,network,storage,foo,bar " 223 }) 224 defer c.Shutdown() 225 226 node := c.Node() 227 if node.Attributes["cpu.frequency"] == "" { 228 t.Fatalf("missing cpu fingerprint module") 229 } 230 } 231 232 func TestClient_Fingerprint_InBlacklist(t *testing.T) { 233 t.Parallel() 234 c := testClient(t, func(c *config.Config) { 235 if c.Options == nil { 236 c.Options = make(map[string]string) 237 } 238 239 // Weird spacing to test trimming. Blacklist cpu. 240 c.Options["fingerprint.blacklist"] = " cpu " 241 }) 242 defer c.Shutdown() 243 244 node := c.Node() 245 if node.Attributes["cpu.frequency"] != "" { 246 t.Fatalf("cpu fingerprint module loaded despite blacklisting") 247 } 248 } 249 250 func TestClient_Fingerprint_OutOfWhitelist(t *testing.T) { 251 t.Parallel() 252 c := testClient(t, func(c *config.Config) { 253 if c.Options == nil { 254 c.Options = make(map[string]string) 255 } 256 257 c.Options["fingerprint.whitelist"] = "arch,consul,env_aws,env_gce,host,memory,network,storage,foo,bar" 258 }) 259 defer c.Shutdown() 260 261 node := c.Node() 262 if node.Attributes["cpu.frequency"] != "" { 263 t.Fatalf("found cpu fingerprint module") 264 } 265 } 266 267 func TestClient_Fingerprint_WhitelistBlacklistCombination(t *testing.T) { 268 t.Parallel() 269 c := testClient(t, func(c *config.Config) { 270 if c.Options == nil { 271 c.Options = make(map[string]string) 272 } 273 274 // With both white- and blacklist, should return the set difference of modules (arch, cpu) 275 c.Options["fingerprint.whitelist"] = "arch,memory,cpu" 276 c.Options["fingerprint.blacklist"] = "memory,nomad" 277 }) 278 defer c.Shutdown() 279 280 node := c.Node() 281 // Check expected modules are present 282 if node.Attributes["cpu.frequency"] == "" { 283 t.Fatalf("missing cpu fingerprint module") 284 } 285 if node.Attributes["cpu.arch"] == "" { 286 t.Fatalf("missing arch fingerprint module") 287 } 288 // Check remainder _not_ present 289 if node.Attributes["memory.totalbytes"] != "" { 290 t.Fatalf("found memory fingerprint module") 291 } 292 if node.Attributes["nomad.version"] != "" { 293 t.Fatalf("found nomad fingerprint module") 294 } 295 } 296 297 func TestClient_Drivers_InWhitelist(t *testing.T) { 298 t.Parallel() 299 c := testClient(t, func(c *config.Config) { 300 if c.Options == nil { 301 c.Options = make(map[string]string) 302 } 303 304 // Weird spacing to test trimming 305 c.Options["driver.raw_exec.enable"] = "1" 306 c.Options["driver.whitelist"] = " raw_exec , foo " 307 }) 308 defer c.Shutdown() 309 310 node := c.Node() 311 if node.Attributes["driver.raw_exec"] == "" { 312 t.Fatalf("missing raw_exec driver") 313 } 314 } 315 316 func TestClient_Drivers_InBlacklist(t *testing.T) { 317 t.Parallel() 318 c := testClient(t, func(c *config.Config) { 319 if c.Options == nil { 320 c.Options = make(map[string]string) 321 } 322 323 // Weird spacing to test trimming 324 c.Options["driver.raw_exec.enable"] = "1" 325 c.Options["driver.blacklist"] = " raw_exec , foo " 326 }) 327 defer c.Shutdown() 328 329 node := c.Node() 330 if node.Attributes["driver.raw_exec"] != "" { 331 t.Fatalf("raw_exec driver loaded despite blacklist") 332 } 333 } 334 335 func TestClient_Drivers_OutOfWhitelist(t *testing.T) { 336 t.Parallel() 337 c := testClient(t, func(c *config.Config) { 338 if c.Options == nil { 339 c.Options = make(map[string]string) 340 } 341 342 c.Options["driver.whitelist"] = "foo,bar,baz" 343 }) 344 defer c.Shutdown() 345 346 node := c.Node() 347 if node.Attributes["driver.exec"] != "" { 348 t.Fatalf("found exec driver") 349 } 350 } 351 352 func TestClient_Drivers_WhitelistBlacklistCombination(t *testing.T) { 353 t.Parallel() 354 c := testClient(t, func(c *config.Config) { 355 if c.Options == nil { 356 c.Options = make(map[string]string) 357 } 358 359 // Expected output is set difference (raw_exec) 360 c.Options["driver.whitelist"] = "raw_exec,exec" 361 c.Options["driver.blacklist"] = "exec" 362 }) 363 defer c.Shutdown() 364 365 node := c.Node() 366 // Check expected present 367 if node.Attributes["driver.raw_exec"] == "" { 368 t.Fatalf("missing raw_exec driver") 369 } 370 // Check expected absent 371 if node.Attributes["driver.exec"] != "" { 372 t.Fatalf("exec driver loaded despite blacklist") 373 } 374 } 375 376 // TestClient_MixedTLS asserts that when a server is running with TLS enabled 377 // it will reject any RPC connections from clients that lack TLS. See #2525 378 func TestClient_MixedTLS(t *testing.T) { 379 t.Parallel() 380 const ( 381 cafile = "../helper/tlsutil/testdata/ca.pem" 382 foocert = "../helper/tlsutil/testdata/nomad-foo.pem" 383 fookey = "../helper/tlsutil/testdata/nomad-foo-key.pem" 384 ) 385 s1, addr := testServer(t, func(c *nomad.Config) { 386 c.TLSConfig = &nconfig.TLSConfig{ 387 EnableHTTP: true, 388 EnableRPC: true, 389 VerifyServerHostname: true, 390 CAFile: cafile, 391 CertFile: foocert, 392 KeyFile: fookey, 393 } 394 }) 395 defer s1.Shutdown() 396 testutil.WaitForLeader(t, s1.RPC) 397 398 c1 := testClient(t, func(c *config.Config) { 399 c.Servers = []string{addr} 400 }) 401 defer c1.Shutdown() 402 403 req := structs.NodeSpecificRequest{ 404 NodeID: c1.Node().ID, 405 QueryOptions: structs.QueryOptions{Region: "global"}, 406 } 407 var out structs.SingleNodeResponse 408 testutil.AssertUntil(100*time.Millisecond, 409 func() (bool, error) { 410 err := c1.RPC("Node.GetNode", &req, &out) 411 if err == nil { 412 return false, fmt.Errorf("client RPC succeeded when it should have failed:\n%+v", out) 413 } 414 return true, nil 415 }, 416 func(err error) { 417 t.Fatalf(err.Error()) 418 }, 419 ) 420 } 421 422 // TestClient_BadTLS asserts that when a client and server are running with TLS 423 // enabled -- but their certificates are signed by different CAs -- they're 424 // unable to communicate. 425 func TestClient_BadTLS(t *testing.T) { 426 t.Parallel() 427 const ( 428 cafile = "../helper/tlsutil/testdata/ca.pem" 429 foocert = "../helper/tlsutil/testdata/nomad-foo.pem" 430 fookey = "../helper/tlsutil/testdata/nomad-foo-key.pem" 431 badca = "../helper/tlsutil/testdata/ca-bad.pem" 432 badcert = "../helper/tlsutil/testdata/nomad-bad.pem" 433 badkey = "../helper/tlsutil/testdata/nomad-bad-key.pem" 434 ) 435 s1, addr := testServer(t, func(c *nomad.Config) { 436 c.TLSConfig = &nconfig.TLSConfig{ 437 EnableHTTP: true, 438 EnableRPC: true, 439 VerifyServerHostname: true, 440 CAFile: cafile, 441 CertFile: foocert, 442 KeyFile: fookey, 443 } 444 }) 445 defer s1.Shutdown() 446 testutil.WaitForLeader(t, s1.RPC) 447 448 c1 := testClient(t, func(c *config.Config) { 449 c.Servers = []string{addr} 450 c.TLSConfig = &nconfig.TLSConfig{ 451 EnableHTTP: true, 452 EnableRPC: true, 453 VerifyServerHostname: true, 454 CAFile: badca, 455 CertFile: badcert, 456 KeyFile: badkey, 457 } 458 }) 459 defer c1.Shutdown() 460 461 req := structs.NodeSpecificRequest{ 462 NodeID: c1.Node().ID, 463 QueryOptions: structs.QueryOptions{Region: "global"}, 464 } 465 var out structs.SingleNodeResponse 466 testutil.AssertUntil(100*time.Millisecond, 467 func() (bool, error) { 468 err := c1.RPC("Node.GetNode", &req, &out) 469 if err == nil { 470 return false, fmt.Errorf("client RPC succeeded when it should have failed:\n%+v", out) 471 } 472 return true, nil 473 }, 474 func(err error) { 475 t.Fatalf(err.Error()) 476 }, 477 ) 478 } 479 480 func TestClient_Register(t *testing.T) { 481 t.Parallel() 482 s1, _ := testServer(t, nil) 483 defer s1.Shutdown() 484 testutil.WaitForLeader(t, s1.RPC) 485 486 c1 := testClient(t, func(c *config.Config) { 487 c.RPCHandler = s1 488 }) 489 defer c1.Shutdown() 490 491 req := structs.NodeSpecificRequest{ 492 NodeID: c1.Node().ID, 493 QueryOptions: structs.QueryOptions{Region: "global"}, 494 } 495 var out structs.SingleNodeResponse 496 497 // Register should succeed 498 testutil.WaitForResult(func() (bool, error) { 499 err := s1.RPC("Node.GetNode", &req, &out) 500 if err != nil { 501 return false, err 502 } 503 if out.Node == nil { 504 return false, fmt.Errorf("missing reg") 505 } 506 return out.Node.ID == req.NodeID, nil 507 }, func(err error) { 508 t.Fatalf("err: %v", err) 509 }) 510 } 511 512 func TestClient_Heartbeat(t *testing.T) { 513 t.Parallel() 514 s1, _ := testServer(t, func(c *nomad.Config) { 515 c.MinHeartbeatTTL = 50 * time.Millisecond 516 }) 517 defer s1.Shutdown() 518 testutil.WaitForLeader(t, s1.RPC) 519 520 c1 := testClient(t, func(c *config.Config) { 521 c.RPCHandler = s1 522 }) 523 defer c1.Shutdown() 524 525 req := structs.NodeSpecificRequest{ 526 NodeID: c1.Node().ID, 527 QueryOptions: structs.QueryOptions{Region: "global"}, 528 } 529 var out structs.SingleNodeResponse 530 531 // Register should succeed 532 testutil.WaitForResult(func() (bool, error) { 533 err := s1.RPC("Node.GetNode", &req, &out) 534 if err != nil { 535 return false, err 536 } 537 if out.Node == nil { 538 return false, fmt.Errorf("missing reg") 539 } 540 return out.Node.Status == structs.NodeStatusReady, nil 541 }, func(err error) { 542 t.Fatalf("err: %v", err) 543 }) 544 } 545 546 func TestClient_UpdateAllocStatus(t *testing.T) { 547 t.Parallel() 548 s1, _ := testServer(t, nil) 549 defer s1.Shutdown() 550 testutil.WaitForLeader(t, s1.RPC) 551 552 c1 := testClient(t, func(c *config.Config) { 553 c.RPCHandler = s1 554 }) 555 defer c1.Shutdown() 556 557 // Wait til the node is ready 558 waitTilNodeReady(c1, t) 559 560 job := mock.Job() 561 alloc := mock.Alloc() 562 alloc.NodeID = c1.Node().ID 563 alloc.Job = job 564 alloc.JobID = job.ID 565 originalStatus := "foo" 566 alloc.ClientStatus = originalStatus 567 568 // Insert at zero so they are pulled 569 state := s1.State() 570 if err := state.UpsertJob(0, job); err != nil { 571 t.Fatal(err) 572 } 573 if err := state.UpsertJobSummary(100, mock.JobSummary(alloc.JobID)); err != nil { 574 t.Fatal(err) 575 } 576 state.UpsertAllocs(101, []*structs.Allocation{alloc}) 577 578 testutil.WaitForResult(func() (bool, error) { 579 ws := memdb.NewWatchSet() 580 out, err := state.AllocByID(ws, alloc.ID) 581 if err != nil { 582 return false, err 583 } 584 if out == nil { 585 return false, fmt.Errorf("no such alloc") 586 } 587 if out.ClientStatus == originalStatus { 588 return false, fmt.Errorf("Alloc client status not updated; got %v", out.ClientStatus) 589 } 590 return true, nil 591 }, func(err error) { 592 t.Fatalf("err: %v", err) 593 }) 594 } 595 596 func TestClient_WatchAllocs(t *testing.T) { 597 t.Parallel() 598 ctestutil.ExecCompatible(t) 599 s1, _ := testServer(t, nil) 600 defer s1.Shutdown() 601 testutil.WaitForLeader(t, s1.RPC) 602 603 c1 := testClient(t, func(c *config.Config) { 604 c.RPCHandler = s1 605 }) 606 defer c1.Shutdown() 607 608 // Wait til the node is ready 609 waitTilNodeReady(c1, t) 610 611 // Create mock allocations 612 job := mock.Job() 613 alloc1 := mock.Alloc() 614 alloc1.JobID = job.ID 615 alloc1.Job = job 616 alloc1.NodeID = c1.Node().ID 617 alloc2 := mock.Alloc() 618 alloc2.NodeID = c1.Node().ID 619 alloc2.JobID = job.ID 620 alloc2.Job = job 621 622 // Insert at zero so they are pulled 623 state := s1.State() 624 if err := state.UpsertJob(100, job); err != nil { 625 t.Fatal(err) 626 } 627 if err := state.UpsertJobSummary(101, mock.JobSummary(alloc1.JobID)); err != nil { 628 t.Fatal(err) 629 } 630 err := state.UpsertAllocs(102, []*structs.Allocation{alloc1, alloc2}) 631 if err != nil { 632 t.Fatalf("err: %v", err) 633 } 634 635 // Both allocations should get registered 636 testutil.WaitForResult(func() (bool, error) { 637 c1.allocLock.RLock() 638 num := len(c1.allocs) 639 c1.allocLock.RUnlock() 640 return num == 2, nil 641 }, func(err error) { 642 t.Fatalf("err: %v", err) 643 }) 644 645 // Delete one allocation 646 err = state.DeleteEval(103, nil, []string{alloc1.ID}) 647 if err != nil { 648 t.Fatalf("err: %v", err) 649 } 650 651 // Update the other allocation. Have to make a copy because the allocs are 652 // shared in memory in the test and the modify index would be updated in the 653 // alloc runner. 654 alloc2_2 := new(structs.Allocation) 655 *alloc2_2 = *alloc2 656 alloc2_2.DesiredStatus = structs.AllocDesiredStatusStop 657 err = state.UpsertAllocs(104, []*structs.Allocation{alloc2_2}) 658 if err != nil { 659 t.Fatalf("err: %v", err) 660 } 661 662 // One allocations should get de-registered 663 testutil.WaitForResult(func() (bool, error) { 664 c1.allocLock.RLock() 665 num := len(c1.allocs) 666 c1.allocLock.RUnlock() 667 return num == 1, nil 668 }, func(err error) { 669 t.Fatalf("err: %v", err) 670 }) 671 672 // One allocations should get updated 673 testutil.WaitForResult(func() (bool, error) { 674 c1.allocLock.RLock() 675 ar := c1.allocs[alloc2.ID] 676 c1.allocLock.RUnlock() 677 return ar.Alloc().DesiredStatus == structs.AllocDesiredStatusStop, nil 678 }, func(err error) { 679 t.Fatalf("err: %v", err) 680 }) 681 } 682 683 func waitTilNodeReady(client *Client, t *testing.T) { 684 testutil.WaitForResult(func() (bool, error) { 685 n := client.Node() 686 if n.Status != structs.NodeStatusReady { 687 return false, fmt.Errorf("node not registered") 688 } 689 return true, nil 690 }, func(err error) { 691 t.Fatalf("err: %v", err) 692 }) 693 } 694 695 func TestClient_SaveRestoreState(t *testing.T) { 696 t.Parallel() 697 ctestutil.ExecCompatible(t) 698 s1, _ := testServer(t, nil) 699 defer s1.Shutdown() 700 testutil.WaitForLeader(t, s1.RPC) 701 702 c1 := testClient(t, func(c *config.Config) { 703 c.DevMode = false 704 c.RPCHandler = s1 705 }) 706 defer c1.Shutdown() 707 708 // Wait til the node is ready 709 waitTilNodeReady(c1, t) 710 711 // Create mock allocations 712 job := mock.Job() 713 alloc1 := mock.Alloc() 714 alloc1.NodeID = c1.Node().ID 715 alloc1.Job = job 716 alloc1.JobID = job.ID 717 alloc1.Job.TaskGroups[0].Tasks[0].Driver = "mock_driver" 718 task := alloc1.Job.TaskGroups[0].Tasks[0] 719 task.Config["run_for"] = "10s" 720 721 state := s1.State() 722 if err := state.UpsertJob(100, job); err != nil { 723 t.Fatal(err) 724 } 725 if err := state.UpsertJobSummary(101, mock.JobSummary(alloc1.JobID)); err != nil { 726 t.Fatal(err) 727 } 728 if err := state.UpsertAllocs(102, []*structs.Allocation{alloc1}); err != nil { 729 t.Fatalf("err: %v", err) 730 } 731 732 // Allocations should get registered 733 testutil.WaitForResult(func() (bool, error) { 734 c1.allocLock.RLock() 735 ar := c1.allocs[alloc1.ID] 736 c1.allocLock.RUnlock() 737 if ar == nil { 738 return false, fmt.Errorf("nil alloc runner") 739 } 740 if ar.Alloc().ClientStatus != structs.AllocClientStatusRunning { 741 return false, fmt.Errorf("client status: got %v; want %v", ar.Alloc().ClientStatus, structs.AllocClientStatusRunning) 742 } 743 return true, nil 744 }, func(err error) { 745 t.Fatalf("err: %v", err) 746 }) 747 748 // Shutdown the client, saves state 749 if err := c1.Shutdown(); err != nil { 750 t.Fatalf("err: %v", err) 751 } 752 753 // Create a new client 754 logger := log.New(c1.config.LogOutput, "", log.LstdFlags) 755 catalog := consul.NewMockCatalog(logger) 756 mockService := newMockConsulServiceClient() 757 mockService.logger = logger 758 c2, err := NewClient(c1.config, catalog, mockService, logger) 759 if err != nil { 760 t.Fatalf("err: %v", err) 761 } 762 defer c2.Shutdown() 763 764 // Ensure the allocation is running 765 testutil.WaitForResult(func() (bool, error) { 766 c2.allocLock.RLock() 767 ar := c2.allocs[alloc1.ID] 768 c2.allocLock.RUnlock() 769 status := ar.Alloc().ClientStatus 770 alive := status == structs.AllocClientStatusRunning || status == structs.AllocClientStatusPending 771 if !alive { 772 return false, fmt.Errorf("incorrect client status: %#v", ar.Alloc()) 773 } 774 return true, nil 775 }, func(err error) { 776 t.Fatalf("err: %v", err) 777 }) 778 779 // Destroy all the allocations 780 for _, ar := range c2.getAllocRunners() { 781 ar.Destroy() 782 } 783 784 for _, ar := range c2.getAllocRunners() { 785 <-ar.WaitCh() 786 } 787 } 788 789 func TestClient_Init(t *testing.T) { 790 t.Parallel() 791 dir, err := ioutil.TempDir("", "nomad") 792 if err != nil { 793 t.Fatalf("err: %s", err) 794 } 795 defer os.RemoveAll(dir) 796 allocDir := filepath.Join(dir, "alloc") 797 798 client := &Client{ 799 config: &config.Config{ 800 AllocDir: allocDir, 801 }, 802 logger: log.New(os.Stderr, "", log.LstdFlags), 803 } 804 if err := client.init(); err != nil { 805 t.Fatalf("err: %s", err) 806 } 807 808 if _, err := os.Stat(allocDir); err != nil { 809 t.Fatalf("err: %s", err) 810 } 811 } 812 813 func TestClient_BlockedAllocations(t *testing.T) { 814 t.Parallel() 815 s1, _ := testServer(t, nil) 816 defer s1.Shutdown() 817 testutil.WaitForLeader(t, s1.RPC) 818 819 c1 := testClient(t, func(c *config.Config) { 820 c.RPCHandler = s1 821 }) 822 defer c1.Shutdown() 823 824 // Wait for the node to be ready 825 state := s1.State() 826 testutil.WaitForResult(func() (bool, error) { 827 ws := memdb.NewWatchSet() 828 out, err := state.NodeByID(ws, c1.Node().ID) 829 if err != nil { 830 return false, err 831 } 832 if out == nil || out.Status != structs.NodeStatusReady { 833 return false, fmt.Errorf("bad node: %#v", out) 834 } 835 return true, nil 836 }, func(err error) { 837 t.Fatalf("err: %v", err) 838 }) 839 840 // Add an allocation 841 alloc := mock.Alloc() 842 alloc.NodeID = c1.Node().ID 843 alloc.Job.TaskGroups[0].Tasks[0].Driver = "mock_driver" 844 alloc.Job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{ 845 "kill_after": "1s", 846 "run_for": "100s", 847 "exit_code": 0, 848 "exit_signal": 0, 849 "exit_err": "", 850 } 851 852 state.UpsertJobSummary(99, mock.JobSummary(alloc.JobID)) 853 state.UpsertAllocs(100, []*structs.Allocation{alloc}) 854 855 // Wait until the client downloads and starts the allocation 856 testutil.WaitForResult(func() (bool, error) { 857 ws := memdb.NewWatchSet() 858 out, err := state.AllocByID(ws, alloc.ID) 859 if err != nil { 860 return false, err 861 } 862 if out == nil || out.ClientStatus != structs.AllocClientStatusRunning { 863 return false, fmt.Errorf("bad alloc: %#v", out) 864 } 865 return true, nil 866 }, func(err error) { 867 t.Fatalf("err: %v", err) 868 }) 869 870 // Add a new chained alloc 871 alloc2 := alloc.Copy() 872 alloc2.ID = structs.GenerateUUID() 873 alloc2.Job = alloc.Job 874 alloc2.JobID = alloc.JobID 875 alloc2.PreviousAllocation = alloc.ID 876 if err := state.UpsertAllocs(200, []*structs.Allocation{alloc2}); err != nil { 877 t.Fatalf("err: %v", err) 878 } 879 880 // Enusre that the chained allocation is being tracked as blocked 881 testutil.WaitForResult(func() (bool, error) { 882 alloc, ok := c1.blockedAllocations[alloc2.PreviousAllocation] 883 if ok && alloc.ID == alloc2.ID { 884 return true, nil 885 } 886 return false, fmt.Errorf("no blocked allocations") 887 }, func(err error) { 888 t.Fatalf("err: %v", err) 889 }) 890 891 // Change the desired state of the parent alloc to stop 892 alloc1 := alloc.Copy() 893 alloc1.DesiredStatus = structs.AllocDesiredStatusStop 894 if err := state.UpsertAllocs(300, []*structs.Allocation{alloc1}); err != nil { 895 t.Fatalf("err: %v", err) 896 } 897 898 // Ensure that there are no blocked allocations 899 testutil.WaitForResult(func() (bool, error) { 900 _, ok := c1.blockedAllocations[alloc2.PreviousAllocation] 901 if ok { 902 return false, fmt.Errorf("blocked evals present") 903 } 904 return true, nil 905 }, func(err error) { 906 t.Fatalf("err: %v", err) 907 }) 908 909 // Destroy all the allocations 910 for _, ar := range c1.getAllocRunners() { 911 ar.Destroy() 912 } 913 914 for _, ar := range c1.getAllocRunners() { 915 <-ar.WaitCh() 916 } 917 } 918 919 func TestClient_UnarchiveAllocDir(t *testing.T) { 920 t.Parallel() 921 dir, err := ioutil.TempDir("", "") 922 if err != nil { 923 t.Fatalf("err: %v", err) 924 } 925 defer os.RemoveAll(dir) 926 927 if err := os.Mkdir(filepath.Join(dir, "foo"), 0777); err != nil { 928 t.Fatalf("err: %v", err) 929 } 930 dirInfo, err := os.Stat(filepath.Join(dir, "foo")) 931 if err != nil { 932 t.Fatalf("err: %v", err) 933 } 934 f, err := os.Create(filepath.Join(dir, "foo", "bar")) 935 if err != nil { 936 t.Fatalf("err: %v", err) 937 } 938 if _, err := f.WriteString("foo"); err != nil { 939 t.Fatalf("err: %v", err) 940 } 941 if err := f.Chmod(0644); err != nil { 942 t.Fatalf("err: %v", err) 943 } 944 fInfo, err := f.Stat() 945 if err != nil { 946 t.Fatalf("err: %v", err) 947 } 948 f.Close() 949 if err := os.Symlink("bar", filepath.Join(dir, "foo", "baz")); err != nil { 950 t.Fatalf("err: %v", err) 951 } 952 linkInfo, err := os.Lstat(filepath.Join(dir, "foo", "baz")) 953 if err != nil { 954 t.Fatalf("err: %v", err) 955 } 956 957 buf := new(bytes.Buffer) 958 tw := tar.NewWriter(buf) 959 960 walkFn := func(path string, fileInfo os.FileInfo, err error) error { 961 // Include the path of the file name relative to the alloc dir 962 // so that we can put the files in the right directories 963 link := "" 964 if fileInfo.Mode()&os.ModeSymlink != 0 { 965 target, err := os.Readlink(path) 966 if err != nil { 967 return fmt.Errorf("error reading symlink: %v", err) 968 } 969 link = target 970 } 971 hdr, err := tar.FileInfoHeader(fileInfo, link) 972 if err != nil { 973 return fmt.Errorf("error creating file header: %v", err) 974 } 975 hdr.Name = fileInfo.Name() 976 tw.WriteHeader(hdr) 977 978 // If it's a directory or symlink we just write the header into the tar 979 if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0) { 980 return nil 981 } 982 983 // Write the file into the archive 984 file, err := os.Open(path) 985 if err != nil { 986 return err 987 } 988 defer file.Close() 989 990 if _, err := io.Copy(tw, file); err != nil { 991 return err 992 } 993 994 return nil 995 } 996 997 if err := filepath.Walk(dir, walkFn); err != nil { 998 t.Fatalf("err: %v", err) 999 } 1000 tw.Close() 1001 1002 dir1, err := ioutil.TempDir("", "") 1003 if err != nil { 1004 t.Fatalf("err: %v", err) 1005 } 1006 defer os.RemoveAll(dir1) 1007 1008 c1 := testClient(t, func(c *config.Config) { 1009 c.RPCHandler = nil 1010 }) 1011 defer c1.Shutdown() 1012 1013 rc := ioutil.NopCloser(buf) 1014 1015 c1.migratingAllocs["123"] = newMigrateAllocCtrl(mock.Alloc()) 1016 if err := c1.unarchiveAllocDir(rc, "123", dir1); err != nil { 1017 t.Fatalf("err: %v", err) 1018 } 1019 1020 // Ensure foo is present 1021 fi, err := os.Stat(filepath.Join(dir1, "foo")) 1022 if err != nil { 1023 t.Fatalf("err: %v", err) 1024 } 1025 if fi.Mode() != dirInfo.Mode() { 1026 t.Fatalf("mode: %v", fi.Mode()) 1027 } 1028 1029 fi1, err := os.Stat(filepath.Join(dir1, "bar")) 1030 if err != nil { 1031 t.Fatalf("err: %v", err) 1032 } 1033 if fi1.Mode() != fInfo.Mode() { 1034 t.Fatalf("mode: %v", fi1.Mode()) 1035 } 1036 1037 fi2, err := os.Lstat(filepath.Join(dir1, "baz")) 1038 if err != nil { 1039 t.Fatalf("err: %v", err) 1040 } 1041 if fi2.Mode() != linkInfo.Mode() { 1042 t.Fatalf("mode: %v", fi2.Mode()) 1043 } 1044 }